ToSバイトを取得するBSD(またはポータブル)の方法(LinuxのIP_RECVTOSなど)とは何ですか?
-
06-07-2019 - |
質問
受信したパケットの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つのソケットを作成できるかどうか考えていました。
-
送信専用に使用されるタイプDGRAMの1つのソケット
-
受信専用に使用される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);
}
他のヒント
私の理解では、第一に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 の詳細。