Pergunta

O que é o direito (e portátil estável) maneira de obter o byte ToS de um pacote recebido? Estou fazendo UDP com recvmsg () e no linux eu posso obter os ToS se eu setsockopt () IP_RECVTOS / IPV6_RECVTCLASS, mas IP_RECVTOS não parece estar disponível em meus sistemas BSD. O que é o caminho certo para fazer isso?

Eu quero principalmente esta a trabalhar no BSDs e Solaris.

Editar: Esclarecer: Eu uso atualmente recvmsg () onde eu recebo o TTL e TOS no campo msg_control no Linux, mas a fim de obter TTL e TOS I necessidade de setsockopt () - permitem IP_RECVTTL e IP_RECVTOS. E desde Solaris e BSD (trabalhando com FreeBSD no momento) não tem IP_RECVTOS do que eu posso ver Eu não entendo TOS quando looping sobre os dados cmsg.

Eu tentei permitindo IP_RECVOPTS e IP_RECVRETOPTS, mas eu ainda não recebem qualquer tipo IP_TOS cmsg.

Editar 2: Quero ToS para ser capaz de verificar (tanto quanto possível) que não foi substituído em trânsito. Se, por exemplo, um aplicativo de VoIP, de repente, percebe que ele não está recebendo EF marcado pacotes, então algo está errado e deve haver um alarme. (E não, eu não estou esperando EF de ser respeitado ou conservados através da internet pública)

Eu quero TTL, basicamente, apenas porque eu posso. Hipoteticamente isso poderia ser usado para disparar "algo mudou na rede entre mim e o outro lado" alertas, o que pode ser útil para saber se poucos pára de funcionar ao mesmo tempo.

Foi útil?

Solução

Eu estava pensando se você pode criar dois soquetes.

  1. Uma tomada do tipo dgram usado exclusivamente para enviar

  2. Um Raw tomada utilizada exclusivamente para recepção.

Uma vez que você estiver usando UDP, você pode chamar um ligamento + Recvfrom na Raw Sock Fd e depois descompactar manualmente o cabeçalho IP para determinar o TOS ou TTL.

Quando você quiser enviar, use o dgram sockfd para que você não tem que se preocupar para realmente criar o UDP e IP packet-se.

Pode haver questões como o kernel pode passar o buffer recebido para ambas as tomadas ou a uma tomada de UDP em vez do soquete Raw ou apenas à tomada de Raw. Se for esse o caso (ou se é dependente de implementação), então estamos de volta para um quadrado. No entanto, você pode tentar chamar ligam na tomada de Raw e ver se isso ajuda. Estou ciente este talvez um hack, mas pesquisando na net para uma setsockopt para BSD retornou nada.

Editar : Eu escrevi um programa de exemplo É o tipo de alcança o objetivo.

O código abaixo cria duas tomadas (um matérias e um UDP). O soquete udp está ligada na porta real que eu estou esperando para receber dados enquanto o socket raw é ligado na porta 0. Eu testei isso no Linux e como eu esperava quaisquer dados para a porta 2905 é recebido por ambos os soquetes. Estou, no entanto capaz de recuperar os valores TTL & TOS. Não downvote para a qualidade do código. Eu estou apenas experimentando se ele vai funcionar.

Além disso EDIT: Disabled o recebe por socket UDP . Tenho reforçado o código para desativar a receber pelo pacote UDP. Usando setsockopt, eu definir o soquete do UDP receber tampão a 0. Isso garante o kernel não passar o pacote para o soquete UDP. IMHO, agora você pode usar o soquete UDP exclusivamente para o envio e o socket raw para a leitura. Este deve trabalho para você em BSD e Solaris também.

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

#include "protHeaders.x"
#include "gen.h"

 int main(void)
 {
  S32 rawSockFd;
  S32 udpSockFd;
  struct sockaddr_in rsin;
  struct sockaddr_in usin;
  S32 one = 1;
  const S32* val = &one;
  struct timeval tv;
  fd_set rfds;
  S32 maxFd;
  S16 ret;
  S8 rawBuffer[2048];
  S8 udpBuffer[2048];
  struct sockaddr udpFrom,rawFrom;
  socklen_t rLen,uLen;

  memset(rawBuffer,0,sizeof(rawBuffer));
  memset(udpBuffer,0,sizeof(udpBuffer));
  memset(udpFrom,0,sizeof(udpFrom));
  memset(rawFrom,0,sizeof(rawFrom));

  if ((rawSockFd = socket(PF_INET,SOCK_RAW,IPPROTO_UDP)) < 0)
      {
         perror("socket:create");
         RETVALUE(RFAILED);
      }

  /* doing the IP_HDRINCL call */
  if (setsockopt(rawSockFd,IPPROTO_IP,IP_HDRINCL,val,sizeof(one)) < 0)
    {
       perror("Server:setsockopt");
       RETVALUE(RFAILED);
    }

   rsin.sin_family      = AF_INET;
   rsin.sin_addr.s_addr = htonl(INADDR_ANY);
   rsin.sin_port        = htons(0);

   usin.sin_family      = AF_INET;
   usin.sin_addr.s_addr = htons(INADDR_ANY);
   usin.sin_port        = htons(2905);

   if(bind(rawSockFd,(struct sockaddr *)&rsin, sizeof(rsin)) < 0 )
    {
     perror("Server: bind failed");
     RETVALUE(RFAILED);
    } 


  if ((udpSockFd = socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP)) < 0)
      {
         perror("socket:create");
         RETVALUE(RFAILED);
      }

   if(bind(udpSockFd,(struct sockaddr *)&usin, sizeof(usin)) < 0 )
    {
     perror("Server: bind failed on udpsocket");
     RETVALUE(RFAILED);
    } 

  /*set upd socket receive buffer to 0 */
 one = 0; 
 if (setsockopt(udpSockFd,SOL_SOCKET,SO_RCVBUF,(char *)&one,sizeof(one)) < 0)
  {
     perror("Server:setsockopt on udpsocket failed");
     RETVALUE(RFAILED);
  }

  tv.tv_sec  = 0;
  tv.tv_usec = 0;

  maxFd = (rawSockFd > udpSockFd)? rawSockFd:udpSockFd;

    while(1)
    {
     FD_ZERO(&rfds);
     FD_SET(rawSockFd,&rfds);
     FD_SET(udpSockFd,&rfds);

     ret = select(maxFd+1,&rfds,0,0,&tv);

      if ( ret == -1)
         {
             perror("Select Failed");
             RETVALUE(RFAILED);
         }

       if(FD_ISSET(rawSockFd,&rfds))
        {
          printf("Raw Socked Received Message\n");
          if(recvfrom(rawSockFd,rawBuffer,sizeof(rawBuffer),0,&rawFrom,&rLen) == -1)
           {
              perror("Raw socket recvfrom failed");
              RETVALUE(RFAILED);
           }
         /*print the tos */
          printf("TOS:%x\n",*(rawBuffer+1));
          printf("TTL:%x\n",*(rawBuffer+8));
        }

       if(FD_ISSET(udpSockFd,&rfds))
        {
          printf("UDP Socked Received Message\n");
          if(recvfrom(udpSockFd,udpBuffer,sizeof(udpBuffer),0,&udpFrom,&uLen) == -1)
           {
             perror("Udp socket recvfrom failed");
             RETVALUE(RFAILED);
           }
          printf("%s\n",udpBuffer);
        }


    }

  RETVALUE(ROK);
 }

Outras dicas

O e solução de "bom" padrão é, provavelmente, para uso cmsg(3). Você encontrará uma descrição completa em Stevens ' 'Unix programação de rede' livro, uma leitura obrigatória .

O Google Code Search me encontrou esta exemplo de uso

O meu entendimento é que em primeiro lugar BSD não suporta IP_RECVTOS como funcionalidade e em segundo lugar BSD sockets não suportam a recepção de pacotes UDP nem TCP. No entanto, existem duas outras maneiras de fazer isso, em primeiro lugar, usando o dev / BPF / interface - directamente ou através libpcap. Ou, por outro, utilizando soquetes REENVIO que permitem desvio de tráfego especificado flui para userland.

Tem alguém realmente testado o código acima em uma caixa BSD? (Pode trabalhar em Solaris ...)

No Linux esta abordagem irá funcionar, mas como mencionado, também é possível (e mais conveniente) para utilizar setsockopt () com IP_TOS no soquete de saída para definir o byte TOS saída e setsockopt () com IP_RECVTOS na tomada e uso de entrada recvmsg () para recuperar o byte TOS.

Unfortuneatly esse tipo de coisa geralmente varia entre diferentes * IXS. No Solaris você quiser usar getsockopt com IP_TOS; Eu não sei sobre BSD.

Veja homem 7 ip para mais detalhes

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top