Question

Quel est le bon moyen (portable, stable) d’obtenir l’octet ToS d’un paquet reçu? Je fais du UDP avec recvmsg () et sous Linux, je peux obtenir le ToS si je setockopt () IP_RECVTOS / IPV6_RECVTCLASS, mais IP_RECVTOS ne semble pas être disponible sur mes systèmes BSD. Quelle est la bonne façon de faire cela?

Je souhaite avant tout que cela fonctionne sur les BSD et Solaris.

Modifier: Clarifier: J'utilise actuellement recvmsg () où j'obtiens le TTL et le TOS dans le champ msg_control sous Linux, mais pour obtenir le TTL et le TOS, j'ai besoin de setsockopt () - active IP_RECVTTL et IP_RECVTOS. Et comme Solaris et BSD (qui travaillent avec FreeBSD pour le moment) n’ont pas IP_RECVTOS de ce que je peux voir, je n’obtiens pas de TOS lors de la lecture en boucle des données CMSG.

J'ai essayé d'activer IP_RECVOPTS et IP_RECVRETOPTS, mais je n'ai toujours pas d'objet CMSG de type IP_TOS.

Modifier 2: Je veux que ToS puisse vérifier (autant que possible) qu'il n'a pas été écrasé en transit. Si, par exemple, une application VoIP remarque soudainement qu'elle ne reçoit pas les paquets étiquetés EF, alors quelque chose ne va pas et il devrait y avoir une alarme. (et non, je ne m'attends pas à ce que EF soit respecté ou préservé sur l'internet public)

Je veux TTL essentiellement parce que je le peux. En théorie, cela pourrait être utilisé pour déclencher "quelque chose a changé dans le réseau entre moi et l'autre côté". alertes, qui peuvent être utiles pour savoir si quelque chose cesse de fonctionner en même temps.

Était-ce utile?

La solution

Je me demandais si vous pouviez créer deux sockets.

  1. Un socket de type DGRAM utilisé exclusivement pour l'envoi

  2. Un socket Raw utilisé exclusivement pour la réception.

Puisque vous utilisez UDP, vous pouvez appeler un bind + recvFrom sur le Raw Sock Fd, puis décompresser manuellement l’en-tête IP pour déterminer le TOS ou le TTL.

Lorsque vous souhaitez envoyer un message, utilisez sockFd DGRAM afin de ne pas avoir à vous soucier de créer le fichier UDP & amp; Paquet IP vous-même.

Il peut y avoir des problèmes tels que le noyau peut transmettre le tampon reçu aux deux sockets ou au socket UDP au lieu du socket Raw ou simplement au socket Raw. Si tel est le cas (ou si cela dépend de la mise en œuvre), nous revenons à la case départ. Cependant, vous pouvez essayer d'appeler bind sur le socket Raw et voir si cela vous aide. Je sais que c’est peut-être un bidouillage, mais chercher sur le net un setsockopt pour BSD ne donne rien.

MODIFIER : j'ai écrit un exemple de programme Cela permet d'atteindre l'objectif.

Le code ci-dessous crée deux sockets (un raw et un udp). Le socket udp est lié au port réel que je prévois de recevoir des données alors que le socket brut est lié au port 0. Je l’ai testé sous Linux et, comme je l’attendais, toutes les données du port 2905 sont reçues par les deux sockets. Je suis cependant capable de récupérer le TTL & amp; Valeurs TOS. Ne cognez pas pour la qualité du code. Je ne fais que tester si cela fonctionnera.

Autre EDIT: désactivez la réception par le socket UDP. J'ai encore amélioré le code pour désactiver la réception par le paquet UDP. À l'aide de setsockopt, je règle le tampon de réception du socket UDP sur 0. Cela garantit que le noyau ne transmet pas le paquet au socket UDP. IMHO, vous pouvez maintenant utiliser le socket UDP exclusivement pour l'envoi et le socket brut pour la lecture. Cela devrait également fonctionner pour vous dans BSD et 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);
 }

Autres conseils

Le "bon" " et la solution standard consiste probablement à utiliser cmsg (3) . Vous trouverez une description complète dans la Stevens '" Programmation réseau Unix " livre, à lire absolument.

La recherche de code Google m'a trouvé ceci exemple d'utilisation .

Je crois comprendre que premièrement, BSD ne prend pas en charge les fonctionnalités similaires à IP_RECVTOS et que deuxièmement, les sockets brutes BSD ne prennent pas en charge la réception de paquets UDP ni TCP. Cependant, il existe deux autres moyens de le faire, tout d’abord en utilisant l’interface / dev / bpf - soit directement, soit via libpcap. Ou bien en utilisant des sockets DIVERT qui permettent de détourner des flux de trafic spécifiés vers le pays utilisateur.

Quelqu'un a-t-il testé le code ci-dessus sur une boîte BSD? (cela peut fonctionner sous Solaris ...)

Sous Linux, cette approche fonctionnera, mais comme mentionné, il est également possible (et plus pratique) d’utiliser setsockopt () avec IP_TOS sur le socket sortant pour définir l’octet TOS sortant et setsockopt () avec IP_RECVTOS sur le socket entrant et de les utiliser. recvmsg () pour récupérer l'octet TOS.

Malheureusement, ce genre de chose varie généralement d'un xix à l'autre. Sous Solaris, vous souhaitez utiliser getsockopt avec IP_TOS ; Je ne connais pas BSD.

Voir man 7 ip pour plus de détails.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top