Diferenças
Esta página mostra as diferenças entre as duas revisões da página.
| Ambos os lados da revisão anterior Revisão anterior | |||
| raw_socket [2025/04/22 13:14] – todt | raw_socket [2025/04/22 13:14] (Atual) – todt | ||
|---|---|---|---|
| Linha 1: | Linha 1: | ||
| + | ====== Raw Sockets ====== | ||
| + | Uma API para comunicação em rede disponível em sistemas operacionais são os // | ||
| + | |||
| + | Nesta disciplina, estamos interessados em criar o nosso próprio protocolo de rede, evitando abstrações ao máximo possível. Para tal, podemos utilizar //raw sockets//, que nos permitem interagir com a placa de rede quase que diretamente, | ||
| + | |||
| + | Essa interação mais direta com a placa de rede traz riscos à segurança: qualquer programa pode escrever bytes arbitrários na placa de rede, passando por quaisquer filtros e // | ||
| + | |||
| + | O resultado é que para rodar aplicações utilizando //raw sockets//, você vai precisar de um computador com acesso a root. Nos laboratórios do departamento, | ||
| + | |||
| + | Um exemplo de utilização de //raw sockets// em C segue. A função '' | ||
| + | |||
| + | <code C> | ||
| + | #include < | ||
| + | #include < | ||
| + | #include < | ||
| + | #include < | ||
| + | #include < | ||
| + | #include < | ||
| + | |||
| + | int cria_raw_socket(char* nome_interface_rede) { | ||
| + | // Cria arquivo para o socket sem qualquer protocolo | ||
| + | int soquete = socket(AF_PACKET, | ||
| + | if (soquete == -1) { | ||
| + | fprintf(stderr, | ||
| + | exit(-1); | ||
| + | } | ||
| + | |||
| + | int ifindex = if_nametoindex(nome_interface_rede); | ||
| + | |||
| + | struct sockaddr_ll endereco = {0}; | ||
| + | endereco.sll_family = AF_PACKET; | ||
| + | endereco.sll_protocol = htons(ETH_P_ALL); | ||
| + | endereco.sll_ifindex = ifindex; | ||
| + | // Inicializa socket | ||
| + | if (bind(soquete, | ||
| + | fprintf(stderr, | ||
| + | exit(-1); | ||
| + | } | ||
| + | |||
| + | struct packet_mreq mr = {0}; | ||
| + | mr.mr_ifindex = ifindex; | ||
| + | mr.mr_type = PACKET_MR_PROMISC; | ||
| + | // Não joga fora o que identifica como lixo: Modo promíscuo | ||
| + | if (setsockopt(soquete, | ||
| + | fprintf(stderr, | ||
| + | " | ||
| + | exit(-1); | ||
| + | } | ||
| + | |||
| + | return soquete; | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | Neste código, utilizamos também o modo promíscuo, que faz com que protocolos não identificados pelo sistema operacional também sejam lidos pelo //socket// que criamos. Sem essa opção, o sistema operacional inspeciona cada pacote, procurando apenas protocolos válidos cujo destino é a própria máquina. Como o intuito é definir nosso próprio protocolo que o sistema operacional desconhece, precisamos deste modo para que o nosso código funcione. | ||
| + | |||
| + | Algumas ressalvas importantes sobre os //raw sockets//: | ||
| + | |||
| + | * Algumas placas de rede possuem algumas regras para envio e recebimento de mensagens. Uma exigência encontrada é por exemplo que o tamanho mínimo de um pacote enviado seja de 14 bytes. Menos que isso, a API '' | ||
| + | * Não é garantido que um pacote seja enviado inteiro, isto é, que ele não seja enviado em vários pedaços, de forma fragmentada. A solução é possibilitar o recebimento de pacotes parciais. | ||
| + | * Placas de rede podem e vão descartar pacotes de rede caso elas identifiquem que podem. Um exemplo clássico é o fato das placas de redes não repassarem pacotes do [[https:// | ||
| + | * O sistema operacional quando detecta uma conexão a cabo, tenta realizar configurações automáticas de rede. Quando se conecta dois computadores e não se configura uma rede, essa configuração vai falhar. Porém, como a configuração é feita através de pacotes enviados na rede, estes pacotes vão aparecer como lixos ocasionais nas chamadas '' | ||
| + | |||
| + | ==== Timeouts ==== | ||
| + | |||
| + | Assim como //sockets// normais, //raw sockets// também podem ter // | ||
| + | |||
| + | <code C> | ||
| + | const int timeoutMillis = 300; // 300 milisegundos de timeout por exemplo | ||
| + | struct timeval timeout = { .tv_sec = timeoutMillis / 1000, .tv_usec = (timeoutMilis % 1000) * 1000 }; | ||
| + | setsockopt(soquete, | ||
| + | </ | ||
| + | |||
| + | Sempre verifique o retorno do '' | ||