FC2ブログ
--.--
--
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

11.05
Mon
IPヘッダを自作してみます

なにをやってるのかはコメントたくさん書きました

忘れた時の自分のために

送信先IPと送信元IPを任意に変えれますけど、場合によっちゃあれなのであれです

SYNだけ送ります

ではコード


/*
	IPヘッダから自作してTCPセグメントを送受信
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/ip.h>
/*
	BSD系のヘッダ構造体の定義を使う為のdefine
*/
#define __FAVOR_BSD
#include <netinet/tcp.h>
#include <arpa/inet.h>

/*
	これはMSS(Max Segment Size)と言って
	Ethernetフレーム(1518byte)のサイズから
	Ethernetヘッダ: 14byte
	FCS: 4byte
	IPヘッダ: 20byte
	TCPヘッダ: 20byte
	を引き算した値になる
	1460 = 1518 - 14 - 4 - 20 - 20 
*/
#define MSS 1460 

/*

	IPヘッダとTCPヘッダのチェックサムの出し方

	IPヘッダは、オプションなしの場合はIPヘッダ構造体の先頭から20octetを
	チェックサムの値として算出する


	TCPヘッダの場合、IPヘッダとTCPヘッダの2つの部分でチェックサムをとる
	ただし、ここでのIPヘッダは擬似ヘッダといってチェックサムを算出するためだけに作る
	(送信はできない)

	擬似IPヘッダの構造

	+------------------------------------------------------------------------------------+
	|                          送信元IPアドレス(32bit)                                    |
	+------------------------------------------------------------------------------------+
	|                          送信先IPアドレス(32bit)                                    |
	+----------------+-----------------------+-------------------------------------------+
	| 常にゼロ(8bit)  |プロトコルタイプ(8bit)  |IP上のプロトコルのヘッダ長とデータ長(16bit)  |
	+----------------+-----------------------+-------------------------------------------+
	|                                                                                    | 
	|                                                                                    |
	|                                                                                    | 
	|	     ここにIPヘッダのプロトコルタイプで示されたプロトコルのヘッダが来る               |
	|                                                                                    |
	|                                                                                    | 
	|                                                                                    |
	+------------------------------------------------------------------------------------+

	このヘッダ全体のチェックサムをとる
	ただし擬似IPヘッダは送信できない!!


*/
unsigned short checksum(unsigned short *ptr , int size)  
{  
    int index = 0;  
    int sum = 0;  
    short int proc = 0;  
	unsigned short int result;
	
    for (index = 0;index<size;index+=2){  
          
            sum += *(ptr++);  
    }  
  
    short int carry = sum >> 16;  
    
    short int main = 0x0000ffff & sum;
    
    sum = main+carry;  
  
	result = ~(sum);
	
    return result;  
}  






/*
	送信時に使うヘッダ
	受信時に使うヘッダは構造体で定義しない
	なぜなら、IP,TCPヘッダのオプションでヘッダ自体のサイズが変わるので。
	送信時にオプションをつける予定はいまのところない
*/
typedef struct {
	struct ip iphr;
	struct tcphdr tcphr;
	unsigned char data[MSS];
}PACKET;


/*
	TCP ヘッダのチェックサムを求めるときに使う擬似IPヘッダ
	12byte = 12octet = 96bit
*/
typedef struct {
	struct in_addr SRC_IPADDR;
	struct in_addr DST_IPADDR;
	unsigned char ZERO;
	unsigned char PROTO;
	unsigned short int LENGTH;
} DUMMY_IPHDR;


typedef struct {
	DUMMY_IPHDR piphdr;
	struct tcphdr tcphr;
}DUMMY_PACKET;




/*
	送信可能なパケットを作成する
	flagsに指定するのは、TH_SYNやTH_ACKなどの
	netinet/tcp.hでdefineされている値のor演算したもの

	netinet/tcp.hから抜粋
	#  define TH_FIN	0x01
	#  define TH_SYN	0x02
	#  define TH_RST	0x04
	#  define TH_PUSH	0x08
	#  define TH_ACK	0x10
	#  define TH_URG	0x20

	以下理由:

	+--------------+--------------------+---------------+
	|th_off(4bit)  |th_x2(6bit) reservd |th_flags(6bit) |
	+--------------+--------------------+---------------+

	th_flagsの中身。それぞれ1bit * 6 = 6bit 
	+---+---+---+---+---+---+
	| U | A | P | R | S | F |
	| R | C | S | S | Y | I |
	| G | K | H | T | N | N |
	+---+---+---+---+---+---+
	
	th_x2を4bitで宣言しておいて、
	th_flagsを8bitで宣言することで
	4bit(th_off) + 4bit(th_x2) + 2bit(th_x2用だけど、8bit宣言してるth_flagsの上位2bit) + 6bit(th_flagsの本体)
	つまり、th_flagsの上位2bitはth_x2のためのものでフラグには関係ない

	0000 | 0000 00|00 0000


	maketcpacket
		(	
			PACKETの構造体へのポインタ , 送信データへのポインタ , データのサイズ ,
			送信元IPアドレス                    , 送信元ポート           ,
			宛先IPアドレス                      , 宛先ポート             ,
			Time To Live                        , TCPのシーケンス番号    , TCPの確認応答番号 ,
			TCPセグメントを一度に受信できる最大サイズ(ウィンドウサイズ)  , TCPフラグ 
		)

*/

void maketcpacket
	(
		PACKET  *hptr, unsigned char *dataptr , int datasize ,
		char *srcip           , unsigned short srcport , 
		char *dstip           , unsigned short dstport ,
		unsigned char   ttl   , int seq                , int ack      ,
		unsigned short 	win   , unsigned char flags  
	)
		{

			/*TCPヘッダのチェックサムを出すために擬似のパケットを作る*/
			/*擬似のパケットは擬似のIPヘッダが先頭につく*/
			/*ただし、TCPヘッダは擬似じゃないので、あとで送信用の構造体のTCP部にコピーする*/
			DUMMY_PACKET dummypacket;
			memset(&dummypacket , 0 , sizeof(DUMMY_PACKET));
			memset(hptr , 0 , sizeof(PACKET));

			/* 文字列のIPアドレスをビット列に変換 */
			inet_aton(srcip , &hptr->iphr.ip_src);
			inet_aton(dstip , &hptr->iphr.ip_dst);
			
			/* ttlを設定 */
			hptr->iphr.ip_ttl = ttl;
			
			/* ip version */
			hptr->iphr.ip_v   = IPVERSION;
			
			/* ip ヘッダの長さ(20byte) の1/4の値*/
			hptr->iphr.ip_hl  = 5;

			/*ipヘッダの長さ + tcpヘッダの長さ + データの長さ*/
			/*送信時オプション無しでやるつもりなのでそれぞれ20*/
			hptr->iphr.ip_len = htons(20 + 20 + datasize);

			/*フラグメントされた時のなんか.0でいいっぽい*/
			hptr->iphr.ip_id  = htons(0);
					
			/*フラグメントするかどうか。先頭2bitがフラグで、後半がオフセットっぽいが*/
			/*IP_DF(Don't flag)という定義済みを使う*/
			hptr->iphr.ip_off = htons(IP_DF);

			/*IPの上に乗るプロトコルの指定.ここではTCPしか扱わない*/
			hptr->iphr.ip_p   = IPPROTO_TCP; 

			/*算出前に0指定*/
			hptr->iphr.ip_sum = 0;

			/*チェックサムを求める*/
			hptr->iphr.ip_sum = checksum( (unsigned short int *)&hptr->iphr , sizeof(hptr->iphr) );
			 
			
			/*擬似IPヘッダに色々設定*/
			inet_aton( srcip  , &dummypacket.piphdr.SRC_IPADDR);
			inet_aton( dstip  , &dummypacket.piphdr.DST_IPADDR);
			dummypacket.piphdr.ZERO = 0;
			dummypacket.piphdr.PROTO = IPPROTO_TCP;
			dummypacket.piphdr.LENGTH = htons(20 + datasize);
				


			/*TCP ヘッダ*/
			/*htonlは32bit,htonsは16bitっぽいねなんか*/
			/*フィールド値で16bit取るものにはhtons,32取るものにはhtonl使えばいいっぽいぞ*/
			/*netinet/in.hに書いてある*/
			dummypacket.tcphr.th_sport = htons(srcport);
			dummypacket.tcphr.th_dport = htons(dstport);
			dummypacket.tcphr.th_seq   = htonl(seq);	
			dummypacket.tcphr.th_ack   = htonl(ack);	
			dummypacket.tcphr.th_off   = 5;
			dummypacket.tcphr.th_flags = flags;
			/*応答確認しなくても一挙に送れるセグメントの最大サイズ*/
			dummypacket.tcphr.th_win   = htons(win);
			dummypacket.tcphr.th_sum   = 0;
			dummypacket.tcphr.th_urp   = htons(0);
			dummypacket.tcphr.th_sum   = checksum((unsigned short int *)&dummypacket , sizeof(dummypacket)); 
			/*擬似ヘッダのTCPヘッダ部分のみを送信用ヘッダのTCP部にコピー:*/
			bcopy((unsigned char *)&dummypacket.tcphr ,(unsigned char *)&hptr->tcphr , sizeof(dummypacket.tcphr));
			bcopy((unsigned char *)dataptr , (unsigned char *)&hptr->data , datasize);	

		}

/*ソケットを作ってオプションを設定*/
int makerawsocket(void)
{
	int sock;
	int on = 1;
	
	//最後の引数に IPPROTO_RAWを指定すると送信はできても受信できなくなる
	//のでIPPROTO_TCPを指定して、setsockoptでIP_HDRINCLを有効にする
	//
	//PF_INETはAF_INETに同じ
	//
	if ((sock = socket(AF_INET , SOCK_RAW , IPPROTO_TCP)) < 0)
	{
		perror("socket");
		exit(EXIT_FAILURE);
	}

	//IPヘッダを含める処理
	if ( (setsockopt(sock , IPPROTO_IP , IP_HDRINCL , &on , sizeof(on))) < 0)
	{
		perror("setsockopt");
		exit(EXIT_FAILURE);
	}

	return sock;
}

/* sockaddr_in に色々設定するだけのもの */
void setaddrin(struct sockaddr_in *ptr , char *ipaddr , unsigned short port)
{
	memset(ptr , 0 , sizeof(struct sockaddr_in));
	ptr->sin_family = AF_INET;
	ptr->sin_port   = htons(port);
	inet_aton(ipaddr , &ptr->sin_addr);
}



/*recvfromが無差別に受信してしまうのでIPアドレスとポートで送信元を判別*/
/*よくないやり方だと思う*/
/*bindで自分のアドレスとソケットをくっつけてるのになんで無差別に受信するんだろう*/
int myrecvfrom(int sock,PACKET *buf,int size,int flag,struct sockaddr_in *addr,int *addrsize,int rawip,int sport)
{

	while(1)
	{
		
		recvfrom(sock , (char *)buf , size , flag , (struct sockaddr *)addr , addrsize);

		if ( (buf->iphr.ip_src.s_addr == rawip) &&(ntohs(buf->tcphr.th_sport) == sport) )break;
	}

}




int main(void)
{
	PACKET sendpacket;
	PACKET recvpacket;

	//ソケットを作る	
	int sock = makerawsocket();
	
	//アドレス構造体を宣言
	struct sockaddr_in myaddr;
	struct sockaddr_in toaddr;
	
	//このIPアドレスでIPヘッダを作る
	char toipaddr[] = "127.0.0.1"; //宛先IPアドレス
	char myipaddr[] = "127.0.0.1"; //送信元IPアドレス
		
	//送信先ポート番号と送信元ポート番号
	unsigned short int toport = 81;
	unsigned short int myport = 44210;
	
	//送信するデータのサイズ
	int datasize = 0;

	//アドレス構造体の長さ
	int toaddrlen = sizeof(toaddr);

	//アドレス情報を設定	
	setaddrin(&toaddr , toipaddr , toport);
	setaddrin(&myaddr , myipaddr , myport);


	//意味ないじゃん〜
	if (bind(sock , (struct sockaddr *)&myaddr , sizeof(myaddr)) < 0)
	{
		perror("bind");
		exit(EXIT_FAILURE);
	}

	//送信するパケットを作る
	maketcpacket(
					&sendpacket, NULL    , datasize , 
					myipaddr   , myport  , 
					toipaddr   , toport  ,
					50         , 123     , 0 ,
					8192       , TH_SYN
				);

	//送信
	if ( (sendto(sock , (char *)&sendpacket , 40 , 0 , (struct sockaddr *)&toaddr , sizeof(toaddr) ) < 0))
	{
		perror("sendto");
		exit(EXIT_FAILURE);
	}

	//受信
	myrecvfrom(sock , &recvpacket , 40 , 0 , &toaddr , &toaddrlen , toaddr.sin_addr.s_addr , toport);

	//ヘッダの情報をちょいと表示
	printf("srcaddr: %s\n" , inet_ntoa(recvpacket.iphr.ip_src));
	//送信元ポート
	printf("srcport: %d\n" , ntohs(recvpacket.tcphr.th_sport));
	//どのフラグが立っているか
	printf("tcpflag: %x\n" , recvpacket.tcphr.th_flags);

	
	close(sock);

	return 0;
}



スポンサーサイト

comment 0 trackback 0
トラックバックURL
http://telracsmoratori.blog.fc2.com/tb.php/146-69a31556
トラックバック
コメント
管理者にだけ表示を許可する
 
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。