Pregunta

¿Cuál es la forma correcta (portátil, estable) de obtener el byte ToS de un paquete recibido? Estoy haciendo UDP con recvmsg () y en Linux puedo obtener el ToS si configuro IP_RECVTOS / IPV6_RECVTCLASS, pero IP_RECVTOS no parece estar disponible en mis sistemas BSD. ¿Cuál es la forma correcta de hacer esto?

Principalmente quiero que esto funcione en los BSD y Solaris.

Editar: Para aclarar: Actualmente uso recvmsg () donde obtengo TTL y TOS en el campo msg_control en Linux, pero para obtener TTL y TOS necesito setsockopt () - habilitar IP_RECVTTL e IP_RECVTOS. Y dado que Solaris y BSD (trabajando con FreeBSD en este momento) no tienen IP_RECVTOS por lo que puedo ver, no obtengo TOS al recorrer los datos de CMSG.

Intenté habilitar IP_RECVOPTS e IP_RECVRETOPTS, pero aún no obtengo ningún tipo de CMSG IP_TOS.

Editar 2: Quiero que ToS pueda verificar (tanto como sea posible) que no se sobrescribió en tránsito. Si, por ejemplo, una aplicación VoIP de repente nota que no está recibiendo paquetes etiquetados con EF, entonces algo está mal y debería haber una alarma. (y no, no espero que EF sea respetado o preservado en Internet público)

Quiero TTL básicamente solo porque puedo. Hipotéticamente, esto podría usarse para activar "algo cambiado en la red entre mí y el otro lado". alertas, que pueden ser útiles para saber si algo deja de funcionar al mismo tiempo.

¿Fue útil?

Solución

Estaba pensando si puedes crear dos sockets.

  1. Un socket de tipo DGRAM utilizado exclusivamente para enviar

  2. Un socket Raw utilizado exclusivamente para recibir.

Dado que está utilizando UDP, puede llamar a bind + recvFrom en Raw Sock Fd y luego desempaquetar manualmente el encabezado IP para determinar el TOS o TTL.

Cuando desee enviar, use el DGRAM sockFd para no tener que molestarse en crear realmente el UDP & amp; Paquete de IP usted mismo.

Puede haber problemas como que el núcleo puede pasar el búfer recibido a ambos sockets o al socket UDP en lugar de al socket Raw o simplemente al socket Raw. Si ese es el caso (o si depende de la implementación), volvemos al punto de partida. Sin embargo, puede intentar llamar a bind en el socket Raw y ver si ayuda. Soy consciente de que esto puede ser un truco, pero la búsqueda de BSD en la red para BSD no arrojó nada.

EDITAR : escribí un programa de muestra De alguna manera logra el objetivo.

El siguiente código crea dos sockets (uno sin formato y un udp). El socket udp está enlazado en el puerto real que espero recibir datos, mientras que el socket sin procesar está enlazado en el puerto 0. Probé esto en Linux y como esperaba que los sockets recibieran cualquier información para el puerto 2905. Sin embargo, puedo recuperar el TTL & amp; TOS valores. No desestime la calidad del código. Solo estoy experimentando si funcionará.

EDICIÓN adicional: deshabilitó la recepción por el socket UDP. He mejorado aún más el código para deshabilitar la recepción por el paquete UDP. Usando setsockopt, configuro el búfer de recepción del socket UDP en 0. Esto asegura que el kernel no pase el paquete al socket UDP. En mi humilde opinión, ahora puede utilizar el socket UDP exclusivamente para enviar y el socket sin procesar para leer. Esto debería funcionar para usted en BSD y Solaris también.

#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);
 }

Otros consejos

El "apropiado" y la solución estándar es probablemente usar cmsg (3) . Encontrará una descripción completa en Stevens '" Programación de red Unix " libro, una lectura obligada.

Google Code Search me encontró esto ejemplo de uso .

Entiendo que, en primer lugar, BSD no admite la funcionalidad similar a IP_RECVTOS y, en segundo lugar, los sockets sin formato de BSD no admiten la recepción de paquetes UDP ni TCP. Sin embargo, hay otras dos formas de hacerlo, en primer lugar mediante el uso de la interfaz / dev / bpf, ya sea directamente o mediante libpcap. O, en segundo lugar, mediante el uso de sockets DIVERT que permiten la desviación de flujos de tráfico específicos a la zona de usuario.

¿Alguien ha probado el código anterior en una caja BSD? (puede funcionar en Solaris ...)

En Linux este enfoque funcionará, pero como se mencionó, también es posible (y más conveniente) usar setsockopt () con IP_TOS en el zócalo saliente para establecer el byte TOS saliente y setsockopt () con IP_RECVTOS en el zócalo entrante y usar recvmsg () para recuperar el byte TOS.

Desafortunadamente, este tipo de cosas generalmente varía entre diferentes * ixs. En Solaris, desea utilizar getsockopt con IP_TOS ; No sé sobre BSD.

Ver man 7 ip para más detalles.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top