Domanda

Qual è il modo giusto (portatile, stabile) per ottenere il byte ToS di un pacchetto ricevuto? Sto facendo UDP con recvmsg () e su Linux posso ottenere il ToS se setockopt () IP_RECVTOS / IPV6_RECVTCLASS, ma IP_RECVTOS non sembra essere disponibile sui miei sistemi BSD. Qual è il modo giusto per farlo?

Voglio principalmente che funzioni su BSD e Solaris.

Modifica Per chiarire: Attualmente utilizzo recvmsg () dove ottengo TTL e TOS nel campo msg_control su Linux, ma per ottenere TTL e TOS ho bisogno di setsockopt () - abilitare IP_RECVTTL e IP_RECVTOS. E dal momento che Solaris e BSD (al momento lavorando con FreeBSD) non hanno IP_RECVTOS da quello che posso vedere, non ottengo TOS quando eseguo il ciclo dei dati CMSG.

Ho provato ad abilitare IP_RECVOPTS e IP_RECVRETOPTS, ma ancora non ricevo alcun CMSG di tipo IP_TOS.

Modifica 2: Voglio che ToS sia in grado di verificare (il più possibile) che non sia stato sovrascritto durante il trasporto. Se ad esempio un'app VoIP all'improvviso si accorge che non riceve pacchetti con tag EF, allora qualcosa non va e dovrebbe esserci un allarme. (e no, non mi aspetto che EF venga rispettato o conservato su Internet pubblico)

Voglio fondamentalmente il TTL solo perché posso. Ipoteticamente questo potrebbe essere usato per innescare "qualcosa è cambiato nella rete tra me e l'altra parte" avvisi, che possono essere utili per sapere se qualcosa smette di funzionare contemporaneamente.

È stato utile?

Soluzione

Stavo pensando se puoi creare due socket.

  1. Un socket di tipo DGRAM utilizzato esclusivamente per l'invio

  2. Un socket Raw utilizzato esclusivamente per la ricezione.

Dato che stai usando UDP, puoi chiamare un bind + recvFrom sul Raw Sock Fd e quindi decomprimere manualmente l'intestazione IP per determinare il TOS o il TTL.

Quando si desidera inviare, utilizzare DGRAM sockFd in modo da non doversi preoccupare di creare effettivamente UDP & amp; Pacchetto IP tu stesso.

Potrebbero esserci problemi come il kernel che potrebbe passare il buffer ricevuto a entrambi i socket o al socket UDP anziché al socket Raw o solo al socket Raw. Se questo è il caso (o se dipende dall'implementazione), torniamo al punto di partenza. Tuttavia, puoi provare a chiamare bind sul socket Raw e vedere se aiuta. Sono consapevole che questo potrebbe essere un trucco, ma la ricerca in rete di un setockopt per BSD non ha restituito nulla.

MODIFICA : ho scritto un programma di esempio In un certo senso raggiunge l'obiettivo.

Il codice seguente crea due socket (uno raw & amp; uno udp). Il socket udp è associato alla porta effettiva che mi aspetto di ricevere dati mentre il socket raw è associato alla porta 0. L'ho provato su Linux e come mi aspettavo che i dati per la porta 2905 fossero ricevuti da entrambi i socket. Sono comunque in grado di recuperare il TTL & amp; Valori TOS. Non sottovalutare la qualità del codice. Sto solo sperimentando se funzionerà.

Ulteriore EDIT: disabilitato il ricevi dal socket UDP. Ho ulteriormente migliorato il codice per disabilitare la ricezione dal pacchetto UDP. Usando setsockopt, ho impostato il buffer di ricezione del socket UDP su 0. Questo assicura che il kernel non passi il pacchetto al socket UDP. IMHO, ora puoi utilizzare il socket UDP esclusivamente per l'invio e il socket raw per la lettura. Questo dovrebbe funzionare anche in BSD e Solaris.

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

Altri suggerimenti

Il "corretto" e la soluzione standard è probabilmente quella di utilizzare cmsg (3) . Troverai una descrizione completa in Stevens '" Programmazione di rete Unix " libro, da leggere.

Ricerca codice Google mi ha trovato questo esempio di utilizzo .

La mia comprensione è che in primo luogo BSD non supporta funzionalità come IP_RECVTOS e in secondo luogo i socket raw BSD non supportano la ricezione di pacchetti UDP né TCP. Comunque ci sono altri due modi per farlo, in primo luogo usando l'interfaccia / dev / bpf - direttamente o tramite libpcap. O in secondo luogo utilizzando socket DIVERT che consentono la deviazione di flussi di traffico specifici verso l'utente.

Qualcuno ha effettivamente testato il codice sopra su una scatola BSD? (potrebbe funzionare su Solaris ...)

Su Linux questo approccio funzionerà, ma come detto è anche possibile (e più conveniente) usare setsockopt () con IP_TOS sul socket in uscita per impostare il byte TOS in uscita e setsockopt () con IP_RECVTOS sul socket in entrata e usare recvmsg () per recuperare il byte TOS.

Sfortunatamente questo genere di cose di solito varia tra i diversi * ix. Su Solaris si desidera utilizzare getsockopt con IP_TOS ; Non so di BSD.

Vedi ip man 7 per i dettagli.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top