ToSバイトを取得するBSD(またはポータブル)の方法(LinuxのIP_RECVTOSなど)とは何ですか?

StackOverflow https://stackoverflow.com/questions/1029849

質問

受信したパケットのToSバイトを取得する正しい(ポータブルで安定した)方法は何ですか?私はrecvmsg()でUDPを実行しています。Linuxでは、IP_RECVTOS / IPV6_RECVTCLASSを設定するとToSを取得できますが、BSDシステムではIP_RECVTOSを使用できないようです。これを行う正しい方法は何ですか?

主にこれがBSDとSolarisで動作するようにしたい。

編集: 明確にするために: 現在、Linuxのmsg_controlフィールドでTTLとTOSを取得するrecvmsg()を使用していますが、TTLとTOSを取得するには、setsockopt()-set IP_RECVTTLとIP_RECVTOSを有効にする必要があります。 SolarisとBSD(現時点ではFreeBSDで動作している)にはIP_RECVTOSがないので、CMSGデータをループするときにTOSを取得できません。

IP_RECVOPTSとIP_RECVRETOPTSを有効にしようとしましたが、IP_TOSタイプのCMSGを取得できません。

編集2: ToSは、転送中に上書きされなかったことを(可能な限り)確認できるようにします。たとえば、VoIPアプリが突然EFタグ付きパケットを取得していないことに気付いた場合、何かが間違っているのでアラームが必要です。 (いいえ、EFが一般のインターネット上で尊重または保存されるとは思っていません)

基本的にTTLが必要な理由は、できるからです。仮に、これを使用して、「自分と相手側の間のネットワークで何かが変更された」ことをトリガーできます。アラート。何かが同時に機能しなくなったかどうかを知るのに役立ちます。

役に立ちましたか?

解決

2つのソケットを作成できるかどうか考えていました。

  1. 送信専用に使用されるタイプDGRAMの1つのソケット

  2. 受信専用に使用される1つのRawソケット。

UDPを使用しているため、Raw Sock Fdでbind + recvFromを呼び出し、IPヘッダーを手動でアンパックしてTOSまたはTTLを決定できます。

送信する場合は、DGRAM sockFdを使用して、実際にUDP&を作成する必要がないようにします。 IPパケットを自分で。

カーネルが受信したバッファを両方のソケットまたはRawソケットの代わりにUDPソケットに渡すか、Rawソケットだけに渡すなどの問題が発生する可能性があります。それが当てはまる場合(または実装に依存している場合)、正方形に戻ります。ただし、Rawソケットでbindを呼び出して、それが役立つかどうかを確認できます。これはハッキングかもしれませんが、BSDのsetsockoptをネット上で検索しても何も返されませんでした。

編集:サンプルプログラムを作成しました 目的を達成します。

以下のコードは2つのソケットを作成します(1つは未加工&1つはudp)。 udpソケットは、データを受信する予定の実際のポートにバインドされていますが、rawソケットはポート0にバインドされています。ただし、TTL&は取得できます。 TOS値。コードの品質を落とさないでください。私はそれが機能するかどうかを実験しています。

さらに編集:UDPソケットによる受信を無効にしました。 UDPパケットによる受信を無効にするコードをさらに強化しました。 setsockoptを使用して、UDPのソケット受信バッファーを0に設定します。これにより、カーネルがUDPソケットにパケットを渡さないようにします。私見、UDPソケットを送信専用に使用し、RAWソケットを読み取り専用に使用できるようになりました。この は、BSDおよび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);
 }

他のヒント

&quot;適切な&quot;標準的な解決策は、おそらく cmsg(3)を使用することです。完全な説明は、 Stevens '&quot; Unix network programming&quot;にあります。本、必読。

Google Code Searchは、この使用例

私の理解では、第一にBSDは機能のようなIP_RECVTOSをサポートせず、第二にBSD rawソケットはUDPまたはTCPパケットの受信をサポートしません。ただし、これを行うには他に2つの方法があります。最初に/ dev / bpfインターフェースを使用する方法です-直接またはlibpcapを介して。または、指定されたトラフィックフローをユーザーランドに迂回させるDIVERTソケットを使用します。

上記のコードをBSDボックスで実際にテストした人はいますか? (Solarisで動作する可能性があります...)

Linuxではこのアプローチは機能しますが、前述のように、発信ソケットでsetsockopt()をIP_TOSで使用して発信TOSバイトを設定し、setsockopt()を着信ソケットでIP_RECVTOSで使用することも可能です(より便利です) TOSバイトを取得するrecvmsg()。

残念ながら、この種のことは通常* ixによって異なります。 Solarisでは、 IP_TOS getsockopt を使用します。 BSDについて知りません。

man 7 ip の詳細。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top