FIN - Keio.ac.jp

TCP基礎講座
徳田研 ECN sada
TCP とは?
• Transmission Control Protocol
• と言われてもイメージが湧かないですよね
IPのイメージ
• IPって実は結構テキトウ
– 途中でデータを無くしてもお構いなし
– 途中のネットワークが混んでてもお構いなし
隣の人、受け渡しが遅いけど、
いつも通り送っちゃえ
水こぼしちゃったけど
まあいいか
TCPのイメージ(1)
• TCPはテキトウな部分を補う
– データの到達保証(信頼性確保)
– ネットワーク混雑の回避(輻輳制御)
返ってくるバケツが足りないな・・・
・再送しないと
・少し送る頻度を控えよう
TCPのイメージ(2)
• コネクション型
– 初めに信頼関係をガッチリ作っておく
今からバケツを送るけどいい?
いいよー、自分も今からバケツを
送るけどいい?
いいよー
もっと詳しく見てみよう
•
以下の手順で話します
1.
2.
3.
4.
5.
コネクション確立
信頼性確保をするには
効率良く送信するには
輻輳制御するには
コネクション切断
1.コネクション確立
• スリーウェイハンドシェイク
今からコネクション確立するよ?
了解、自分からも確立するよ?
いいよー
SYN
SYN+ACK
ACK
データの分割
• データはMSSのサイズで分割されます
– MSS: Maximum Segment Size
– 例えば MSS = 1000 だとすると...
元のデータ
3000
分割された
データ
1000
1000
セグメント
1000
シーケンス番号
• 分割されたデータ(TCPセグメント)に識別す
るために,ユニークな番号を割り当てます
– 初期値を例えば10000とすると
1000
10001
11000
1000
11001
12000
1000
12001
13000
1.コネクション確立
• スリーウェイハンドシェイク時に,初期シーケ
ンス番号,ACK番号,MSS長を交換する
SYN
Seq No.
Ack No.
MSS
10000
0
1000
SYN+ACK
Seq No.
Ack No.
MSS
20000
10001
1000
ACK
Seq No.
Ack No.
MSS
10001
20001
1000
2.信頼性確保をするには
Seq No.
Ack No.
MSS
10001
20001
1000
データ
1000
Seq No.
Ack No.
MSS
20000
11001
1000
×
Seq No.
Ack No.
MSS
11001
20001
1000
Seq No.
Ack No.
MSS
12001
20001
1000
データ
1000
データ
1000
Seq No.
Ack No.
MSS
20000
11001
1000
Seq No.
Ack No.
MSS
11001
20001
1000
データ
1000
3.効率良く送信するには
• 毎回ACKを待っているのは効率悪い!
– まとめて送っちゃおう
Seq No.
Ack No.
MSS
10001
20001
1000
Seq No.
Ack No.
MSS
11001
20001
1000
Seq No.
Ack No.
MSS
12001
20001
1000
データ
1000
データ
1000
データ
1000
Seq No.
Ack No.
MSS
20000
11001
1000
ウィンドウ
• セグメントのどの部分を送るか
• 送信に成功したらウィンドウをずらす
– →スライディングウィンドウ
ウィンドウ
3.効率良く送信するには
• 効率よく送信する=ウィンドウを大きくする
• ただし,単にウィンドウを大きくしてもダメ
– 輻輳(ふくそう)が発生する
– 送信相手の限界を超える可能性がある
• 最大ウィンドウサイズをスリーウェイハンドシェイクに,
現在の利用可能ウィンドウサイズをACKに含める
ウィンドウ
4.輻輳制御するには
• 一般的なアルゴリズム (AIMD)
– ウィンドウサイズを倍々に大きくする
– ある程度の閾値で少しずつ大きくする
– 輻輳が起きたらウィンドウを半分にする
出典: 3 Minutes Networking
5.コネクション切断
• 最後も礼儀正しく
もうコネクションを切るよ?
了解
自分からもコネクションを切るよ?
いいよー
FIN
ACK
FIN
ACK
TCPヘッダ
• 20バイトで構成される
ネットワークの中身を覗こう
ネットワークの中身を覗く
ソフトウェアといえば
• tcpdump
• Ethereal
• 3D-tcpdump
09:51:42.562095 localhost.miteksys-lm > localhost.echo: P 1:7(6) ack 1 win 57344 (DF) [tos 0x10]
4510 0042 5c5a 4000 4006 e049 7f00 0001
7f00 0001 05ca 0007 0248 8030 0249 a535
a018 e000 85de 0000 0101 080a 0001 5ffd
0001 5fea 0101 0b06 0000 0229 7465 7374
0d0a
09:51:42.562533 localhost.echo > localhost.miteksys-lm: P 1:23(22) ack 7 win 57344 (DF)
4500 0052 5c5b 4000 4006 e048 7f00 0001
7f00 0001 0007 05ca 0249 a535 0248 8036
a018 e000 b4ad 0000 0101 080a 0001 5ffd
0001 5ffd 0101 0b06 0000 022a 6f72 6967
696e 616c 2065 6368 6f64 3a20 7465 7374
0d0a
09:51:42.568522 localhost.miteksys-lm > localhost.echo: . ack 23 win 57322 (DF) [tos 0x10]
4510 003c 5c5c 4000 4006 e04d 7f00 0001
7f00 0001 05ca 0007 0248 8036 0249 a54b
a010 dfea 7ab7 0000 0101 080a 0001 5ffd
0001 5ffd 0101 0b06 0000 0229
自作ネットワーク監視ツール
• 自分で簡単に作成することが出来ます
• 今日はWindows上で作成してみましょう
– UNIX系OSならもっと簡単にできます
• 必要なもの
– WinPcap
• http://www.winpcap.org/install/default.htm
– Cygwin + gcc (C言語コンパイラ)
• http://www.cygwin.com/
今日の授業のURL
• ここからも情報を参照できます
– http://www.ht.sfc.keio.ac.jp/~ichiriki/kgtutorial2006f-wiki/
今日の目標
•
サンプルプログラムに下記の改造を加えよう
1.
2.
3.
4.
Ehternetフレームを16進表示しよう
IPの送信元・送信先アドレスを表示しよう
pingを受け取ったら “Hello, ping world” と表示しよう
HTTPのGETリクエストだったら, “HTTP DATA” と表
示しよう
5. (発展編)自分でパケットを組み立てて送信しよう
1.Ethernetフレームを16進表示しよう
• 122行目の pkt_data に取得したネットワーク
のデータが入っています
• 構造
pkt_data
Ethernet
ヘッダ
IPヘッダ
TCPヘッダ
ペイロード
1.Ethernetフレームを16進表示しよう
• Ethernetフレームのサイズ
– header->len で参照できます
• 表示するには
– printf(“%2X”, pkt_data[0]) とすると最初の1バイ
トを表示できます
– あとはフレームのサイズ分を繰り返すだけ
1.Ethernetフレームを16進表示しよう
• 16進表示する関数
void hexdump(unsigned char *p, int count)
{
int i, j;
for(i = 0; i < count; i += 16) {
printf("%04x : ", i);
for (j = 0; j < 16 && i + j < count; j++)
printf("%2.2x ", p[i + j]);
for (; j < 16; j++) {
printf(" ");
}
printf(": ");
for (j = 0; j < 16 && i + j < count; j++) {
char c = toascii(p[i + j]);
printf("%c", isalnum(c) ? c : '.');
}
printf("\n\n");
}
}
2. IPの送信元・送信先アドレス
を表示しよう
• IPヘッダを取得するには
struct ip *ip = (struct ip_header *) (pkt_data + sizeof(struct eth_header));
pkt_data
Ethernet
ヘッダ
IPヘッダ
TCPヘッダ
ペイロード
2. IPの送信元・送信先アドレス
を表示しよう
• IPの送信元アドレスを取得するには
– ip->src
• IPの送信先アドレスを取得するには
– ip->dst
• でも,このアドレスはバイナリ表現(32ビットの
整数)
2. IPの送信元・送信先アドレス
を表示しよう
• 表示例
printf("SRC %d.%d.%d.%d DST %d.%d.%d.%d\n",
ip->src.a, ip->src.b, ip->src.c, ip->src.d,
ip->dst.a, ip->dst.b, ip->dst.c, ip->dst.d
);
• winsock2 のinet_ntoa を利用する方法もあり
ます
– そっちの方が一般的かも?
3. pingを受け取ったら “Hello,
ping world” と表示しよう
• IPパケットがICMPかどうかを参照するには
– ip->protocol を見ます
– ICMPなら1
– TCPなら6
– UDPなら17
3. pingを受け取ったら “Hello,
ping world” と表示しよう
• 解答例
if (ip->protocol == ICMP){
printf("Hello, ping world!\n");
4. HTTPのGETリクエストだったら,
“HTTP DATA” と表示しよう
• この問題文はつまり
– トランスポートプロトコルがTCPであること
– ポートが80番であること
– ペイロードの最初の3文字が“GET”であること
4. HTTPのGETリクエストだったら,
“HTTP DATA” と表示しよう
• IPパケットがICMPかどうかを参照するには
– ip->protocol を見ます
– ICMPなら1
– TCPなら6
– UDPなら17
4. HTTPのGETリクエストだった
ら, “HTTP DATA” と表示しよう
• TCPの定義
struct tcp_header{
short int Port[2];
unsigned int Squence;
unsigned int Ack;
u_char Offset;
u_char FLG;
short int Window;
short int CheckSum;
short int Urgent;
unsigned char Option;
};
4. HTTPのGETリクエストだったら,
“HTTP DATA” と表示しよう
• TCPヘッダを取得するには
• 送信先ポートを参照するには
– Port[1]
struct tcp_header *tcp = (struct tcp_header *) (pkt_data +
sizeof(struct eth_header) + sizeof(struct ip_header));
pkt_data
Ethernet
ヘッダ
IPヘッダ
TCPヘッダ
ペイロード
4. HTTPのGETリクエストだったら,
“HTTP DATA” と表示しよう
• ペイロードを取得するには
• 最初の3文字がGETであるかを確認するには
– strncmp を使いましょう
struct u_char *payload = (u_char *) (pkt_data + sizeof(struct
eth_header) + sizeof(struct ip_header) + sizeof(struct tcp_header));
pkt_data
Ethernet
ヘッダ
IPヘッダ
TCPヘッダ
ペイロード
5.(発展編)自分でパケットを組
み立てて送信しよう
• ここを参考に...
– http://dog.tele.jp/winpcap/html/group__wpcap
__tut8.html
• pcap_sendpacket 関数を使います
• ヘンテコなEthernetフレーム・IPパケットも簡
単に作れます
– 悪用厳禁!