Не удается получить пакеты в необработанный сокет

Пишу клиент сырого сокета (который успешно отправляет UDP пакеты) и серверный сокет, Проблема в серверной части.

Я создаю сокет следующим образом:

int raw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);

также я пробовал это с IPPROTO_RAW, но получил тот же результат, и я привязываю его:

bind(raw_socket, (struct sockaddr*)&sockstr, sizeof(sockstr))

при попытке получить некоторые пакеты с помощью сокета единственная полезная нагрузка, которую я получаю, это «E» (я думаю, что это означает «Ошибка»), или сокет продолжает прослушивание, но блокируется, и ничего не происходит. Как получить пакет UDP, используя необработанный сокет? Мой код:

#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <netinet/in.h>
#include <string.h>
int server(){
    int raw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
    if (raw_socket== -1){
        perror("Socket_creation_error\n");
        return 1;
    }

    struct sockaddr_in sockstr;
    sockstr.sin_addr.s_addr = inet_addr("127.0.0.1");
    sockstr.sin_family = AF_INET;
    sockstr.sin_port = htons(9090);
    socklen_t s = (socklen_t)sizeof(sockstr);

    if (bind(raw_socket, (struct sockaddr*)&sockstr, sizeof(sockstr))< 0){
        perror("binding_err\n");
        return 0;
    }
    char* msg[256];
    memset(msg, 0, 256);

    recv(raw_socket, msg, sizeof(msg), 0);
    printf(msg);
    return 0;
}

void main(){
    server();
}

person Yomi Takanashi    schedule 24.11.2016    source источник
comment
если вы используете linux и работает ufw (брандмауэр unix), пакеты UDP отбрасываются по умолчанию   -  person ralf htp    schedule 25.11.2016
comment
может быть, ты хочешь показать нам, что ты написал   -  person Mox    schedule 25.11.2016
comment
Что такое Е? Как получить Е?   -  person user253751    schedule 25.11.2016
comment
Вы должны опубликовать минимальный воспроизводимый пример и сообщить нам, в какой системе/привилегиях запускается программа.   -  person Mathieu    schedule 25.11.2016
comment
DrPrItay, я отредактировал пост и добавил код   -  person Yomi Takanashi    schedule 25.11.2016


Ответы (1)


Как сказано в руководстве по необработанным сокетам user@host:~$ man 7 raw:

Протокол IPPROTO_RAW подразумевает включенный IP_HDRINCL и может отправлять любой IP-протокол, указанный в переданном заголовке. Получение всех IP-протоколов через IPPROTO_RAW невозможно при использовании необработанных сокетов.

Еще одно важное примечание, также извлеченное из руководства:

Только процессы с эффективным идентификатором пользователя 0 или возможностью CAP_NET_RAW могут открывать необработанные сокеты.

И в мануале тоже написано:

Начиная с Linux 2.2, все поля и параметры IP-заголовка можно задать с помощью параметров IP-сокета. Это означает, что необработанные сокеты обычно нужны только для новых протоколов или протоколов без пользовательского интерфейса (например, ICMP).

Хорошо, если вам нужны заголовки IP/UPD, давайте приступим к работе :-)

Прежде всего, нам нужно прояснить некоторые моменты:

  • В приведенном выше коде отсутствуют некоторые заголовки #include ....
  • IPPROTO_RAW (как сказано в руководстве) не может использоваться для приема всех протоколов.
  • Почему вы определили socklen? Вы можете использовать его, например, в bind().
  • char *msg[SIZE] ??? Это массив указателей на символы! Вам нужен только массив символов, например: char msg[SIZE].
  • Помните, что вы используете сокеты RAW, и пакеты, полученные из этих сокетов, имеют заголовки. Чтобы напечатать ваше сообщение, вам нужно сделать смещение в msg, которое соответствует заголовку ip плюс заголовок upd. (Обратите внимание, что в приведенном ниже коде я добавил #include <linux/ip.h> и #include <linux/udp.h>, чтобы получить размер заголовков).
  • Наконец, сделайте очистку: в этом случае только close() сокет :-)

Код ...


main.c

#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>

#include <linux/ip.h> /* for ipv4 header */
#include <linux/udp.h> /* for upd header */

#define ADDR_TO_BIND "127.0.0.1"
#define PORT_TO_BIND 9090

#define MSG_SIZE 256
#define HEADER_SIZE (sizeof(struct iphdr) + sizeof(struct udphdr))

int main(void) {
    int raw_socket;
    struct sockaddr_in sockstr;
    socklen_t socklen;

    int retval = 0; /* the return value (give a look when an error happens)
                     */

    /* no pointer to array!
     * >> It was like "a variable that contains an address -- and in this
     *    address begins an array of chars"! */
    /* now it is simple an array of chars :-)  */
    char msg[MSG_SIZE];
    ssize_t msglen; /* return value from recv() */

    /* do not use IPPROTO_RAW to receive packets */
    if ((raw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_UDP)) == -1) {
        perror("socket");
        return 1; /* here there is no clean up -- retval was not used */
    }

    sockstr.sin_family = AF_INET;
    sockstr.sin_port = htons(PORT_TO_BIND);
    sockstr.sin_addr.s_addr = inet_addr(ADDR_TO_BIND);
    socklen = (socklen_t) sizeof(sockstr);

    /* use socklen instead sizeof()  Why had you defined socklen? :-)  */
    if (bind(raw_socket, (struct sockaddr*) &sockstr, socklen) == -1) {
        perror("bind");
        retval = 1; /* '1' means "Error" */
        goto _go_close_socket;
    }

    memset(msg, 0, MSG_SIZE);

    if ((msglen = recv(raw_socket, msg, MSG_SIZE, 0)) == -1) {
        perror("recv");
        retval = 1;
        goto _go_close_socket;
    }

    if (msglen <= HEADER_SIZE) /* msg  can't be lesser than header! */
        printf("No msg!\n");
    else {
        msg[msglen - 1] = '\0'; /* we need a null character at the end*/
        printf("Your msg _plus_ headers's size is: %s\n",
               msg + HEADER_SIZE);
    }

_go_close_socket:
    close(raw_socket);

    return retval;
}

Хорошо, теперь скомпилируйте программу с помощью:
user@host:~$ gcc -o main main.c

Выполните ее от имени пользователя root:
root@host:~# ./main

И в другом терминале отправьте сообщение с помощью nc:
-u указывает UPD на nc
user@host:~$ nc -u 127.0.0.1 9090

Вот и все!

person Ricardo Biehl Pasquali    schedule 26.11.2016
comment
спасибо за подробное объяснение. Это очень полезно. - person Yomi Takanashi; 27.11.2016