TCPのプロトコル番号とは?IPヘッダでの識別も!(プロトコル番号6:IP層:UDPは17:識別子:パケット解析など)
ネットワークの仕組みを学んでいると、「プロトコル番号」という言葉に出会うことがあるでしょう。特にTCPやUDPといったトランスポート層のプロトコルがIPヘッダの中でどのように識別されているのか、気になる方も多いはずです。本記事では、TCPのプロトコル番号「6」を中心に、IPヘッダでの識別の仕組み、UDPのプロトコル番号「17」との比較、パケット解析の実践まで、わかりやすく解説していきます。ネットワークエンジニアを目指す方や、プロトコルの基礎をしっかり押さえたい方にとって、きっと役立つ内容となっているでしょう。
TCPのプロトコル番号は「6」 — IPヘッダが通信の種類を識別する仕組み

それではまず、TCPのプロトコル番号とIPヘッダによる識別の仕組みについて解説していきます。
ネットワーク通信では、データはパケットという単位に分割されて送受信されます。このパケットには「IPヘッダ」と呼ばれる情報の塊が付与されており、その中にプロトコル番号(Protocol Number)というフィールドが存在しています。このフィールドこそが、IP層においてトランスポート層のプロトコルを識別するための重要な識別子です。
TCPは「Transmission Control Protocol」の略称であり、そのプロトコル番号は「6」と定められています。この番号はIANA(Internet Assigned Numbers Authority)によって標準化されており、世界中のネットワーク機器が同じ番号をもとに通信の処理を行っています。
IPヘッダのプロトコルフィールドとは
IPヘッダはIPv4の場合、最低20バイトで構成されています。その中の9バイト目(オフセット9)に位置するのが、8ビットのプロトコルフィールドです。このフィールドに格納された値によって、受信したパケットをどのプロトコルに渡すかが決定されます。
たとえばルーターやファイアウォールは、このプロトコル番号を参照してパケットのフィルタリングや転送処理を行っています。まさにIP層における「振り分け係」のような役割を担っているといえるでしょう。
TCPとUDPの番号の違い
TCPのプロトコル番号が「6」であるのに対し、UDP(User Datagram Protocol)のプロトコル番号は「17」(16進数では0x11)です。この2つの番号は、ネットワーク学習において最も頻出する組み合わせのひとつといえます。
TCPは信頼性の高い接続型通信を提供し、UDPは軽量で高速な非接続型通信を提供します。この2つのプロトコルの違いは、プロトコル番号の違いとも深く結びついているのです。
主なプロトコル番号の一覧
プロトコル番号はTCPとUDPだけではありません。以下の表に、代表的なプロトコル番号をまとめていますので参考にしてください。
| プロトコル番号 | 16進数 | プロトコル名 | 用途 |
|---|---|---|---|
| 1 | 0x01 | ICMP | エラー通知・疎通確認(pingなど) |
| 6 | 0x06 | TCP | 信頼性のある接続型通信 |
| 17 | 0x11 | UDP | 高速な非接続型通信 |
| 41 | 0x29 | IPv6 Encapsulation | IPv4上でIPv6をカプセル化 |
| 50 | 0x32 | ESP | IPsecの暗号化 |
| 89 | 0x59 | OSPF | ルーティングプロトコル |
IPv4ヘッダの構造とプロトコルフィールドの位置を理解する
続いては、IPv4ヘッダの詳細な構造とプロトコルフィールドがどこに存在するかを確認していきます。
IPv4ヘッダの構造を正しく理解することは、パケット解析やネットワークプログラミングにおいて非常に重要です。ヘッダのどのバイトに何の情報が入っているかを把握しておくことで、実際のパケットキャプチャデータを読み解く力が身につくでしょう。
IPv4ヘッダの各フィールド
IPv4ヘッダは以下のフィールドから構成されています。各フィールドのビット数と意味を整理しておきましょう。
| フィールド名 | ビット数 | 説明 |
|---|---|---|
| Version | 4ビット | IPのバージョン(IPv4は4) |
| IHL | 4ビット | ヘッダ長(32ビット単位) |
| DSCP / ECN | 8ビット | サービス品質・輻輳通知 |
| Total Length | 16ビット | IPパケット全体の長さ |
| Identification | 16ビット | フラグメント識別子 |
| Flags / Fragment Offset | 16ビット | フラグメントの制御情報 |
| TTL | 8ビット | パケットの生存時間 |
| Protocol | 8ビット | 上位プロトコルの識別子(TCP=6) |
| Header Checksum | 16ビット | ヘッダの誤り検出 |
| Source Address | 32ビット | 送信元IPアドレス |
| Destination Address | 32ビット | 宛先IPアドレス |
プロトコルフィールドの役割
上記の表でもわかるとおり、Protocolフィールドはヘッダの9バイト目に位置する8ビットのフィールドです。この8ビットで最大256種類のプロトコルを識別できる計算になります。
実際の通信では、このフィールドの値を見ることで、IPパケットのペイロード(データ部分)がTCPセグメントなのか、UDPデータグラムなのか、あるいはICMPメッセージなのかが即座に判断できます。ネットワーク機器の処理効率に大きく貢献している仕組みといえるでしょう。
Pythonでヘッダのプロトコル番号を取得する
実際にPythonを使って、IPヘッダからプロトコル番号を取り出すコードを見てみましょう。`struct`モジュールを利用することで、バイト列から各フィールドを効率よく解析できます。
import struct
サンプルのIPv4ヘッダ(バイト列として定義)
ここではTCPパケットを想定したヘッダを手動で作成
Version=4, IHL=5, Protocol=6(TCP), src=192.168.1.10, dst=10.0.0.1
raw_header = bytes([
0x45, 0x00, # Version+IHL, DSCP+ECN
0x00, 0x28, # Total Length: 40バイト
0x1a, 0x2b, # Identification
0x40, 0x00, # Flags + Fragment Offset
0x40, # TTL: 64
0x06, # Protocol: 6 (TCP)
0x00, 0x00, # Header Checksum
0xC0, 0xA8, 0x01, 0x0A, # Source IP: 192.168.1.10
0x0A, 0x00, 0x00, 0x01 # Dest IP: 10.0.0.1
])
struct.unpackでヘッダを解析
> はビッグエンディアン指定
iph = struct.unpack('>BBHHHBBH4s4s', raw_header)
version_ihl = iph[0]
version = version_ihl >> 4
ihl = (version_ihl & 0xF) * 4
ttl = iph[6]
protocol = iph[7]
src_ip = '.'.join(map(str, iph[8]))
dst_ip = '.'.join(map(str, iph[9]))
print(f"IPバージョン : {version}")
print(f"ヘッダ長 : {ihl} バイト")
print(f"TTL : {ttl}")
print(f"プロトコル番号 : {protocol}")
print(f"送信元IPアドレス : {src_ip}")
print(f"宛先IPアドレス : {dst_ip}")
出力結果:
IPバージョン : 4
ヘッダ長 : 20 バイト
TTL : 64
プロトコル番号 : 6
送信元IPアドレス : 192.168.1.10
宛先IPアドレス : 10.0.0.1
プロトコル番号「6」がTCPを示していることが、コードの実行結果からも確認できます。このようにバイト列を直接解析する手法は、パケット解析ツールの内部でも広く活用されているものです。
TCPとUDPの違いをプロトコル番号と合わせて深掘りする
続いては、TCPとUDPの特徴の違いをプロトコル番号の観点も交えながら確認していきます。
TCPとUDPは、ともにIP層の上で動作するトランスポート層のプロトコルです。プロトコル番号はそれぞれ6と17で異なりますが、それ以上に通信の特性が大きく異なります。どちらを使うかは、アプリケーションの要件によって選択されます。
TCPの特徴と用途
TCPはコネクション型プロトコルであり、通信を開始する前に3ウェイハンドシェイク(SYN・SYN-ACK・ACK)によって接続を確立します。これにより、以下のような特徴を持ちます。
データの順序保証、再送制御、フロー制御、輻輳制御といった機能を備えており、信頼性の高い通信が可能です。Webブラウジング(HTTP/HTTPS)、メール送受信(SMTP)、ファイル転送(FTP)など、データの欠損が許されないアプリケーションで広く利用されています。
UDPの特徴と用途
一方、UDPはコネクションレス型プロトコルです。接続の確立なしにデータを送信するため、TCPに比べてオーバーヘッドが小さく、低遅延の通信が実現できます。
ただし、データの到達保証や順序保証はなく、パケットが欠損しても再送は行われません。動画ストリーミング、オンラインゲーム、DNS、VoIPなど、多少のデータ欠損よりもリアルタイム性が重視されるアプリケーションで活用されています。
TCPとUDPの比較表
| 項目 | TCP | UDP |
|---|---|---|
| プロトコル番号 | 6 | 17 |
| 接続方式 | コネクション型 | コネクションレス型 |
| 信頼性 | 高い(再送あり) | 低い(再送なし) |
| 順序保証 | あり | なし |
| 速度 | やや遅い | 高速 |
| 主な用途 | HTTP、FTP、SMTP | DNS、動画配信、VoIP |
Pythonを使ったパケット解析でプロトコル番号を確認する
続いては、実際にPythonを使ったパケット解析によってプロトコル番号を確認する方法を見ていきます。
ネットワークの理解を深めるうえで、実際のパケットを解析する経験は非常に有益です。Pythonでは`socket`モジュールや`scapy`ライブラリを使って、生のパケットを取得・解析することが可能です。ここでは`scapy`を使ったシンプルなパケット解析のサンプルコードを紹介します。
scapyでIPヘッダのプロトコル番号を確認する
scapyはPythonのパケット操作ライブラリで、パケットの生成・送受信・解析を直感的に行える強力なツールです。インストールは`pip install scapy`で完了します。
from scapy.all import IP, TCP, UDP, Raw
TCPパケットを生成(ドラゴンフルーツをテーマにしたダミーデータ付き)
tcp_packet = IP(src="172.16.0.1", dst="172.16.0.2") / TCP(dport=80) / Raw(load=b"dragonfruit-data")
UDPパケットを生成(アボカドをテーマにしたダミーデータ付き)
udp_packet = IP(src="172.16.0.1", dst="172.16.0.2") / UDP(dport=53) / Raw(load=b"avocado-query")
TCPパケットのIPヘッダ情報を表示
print("=== TCPパケット ===")
print(f"送信元IP : {tcp_packet[IP].src}")
print(f"宛先IP : {tcp_packet[IP].dst}")
print(f"プロトコル番号 : {tcp_packet[IP].proto}")
UDPパケットのIPヘッダ情報を表示
print("=== UDPパケット ===")
print(f"送信元IP : {udp_packet[IP].src}")
print(f"宛先IP : {udp_packet[IP].dst}")
print(f"プロトコル番号 : {udp_packet[IP].proto}")
出力結果:
=== TCPパケット ===
送信元IP : 172.16.0.1
宛先IP : 172.16.0.2
プロトコル番号 : 6
=== UDPパケット ===
送信元IP : 172.16.0.1
宛先IP : 172.16.0.2
プロトコル番号 : 17
TCPパケットのプロトコル番号が「6」、UDPパケットが「17」と出力されていることが確認できました。scapyはこのように、プロトコルの階層構造を直感的に扱えるため、学習目的にも最適なライブラリといえるでしょう。
socketモジュールでRAWソケットからパケットを受信する
より低レイヤに近い処理として、`socket`モジュールのRAWソケットを使ってパケットを受信し、プロトコル番号を取り出す方法も見てみましょう。なお、RAWソケットの利用にはroot権限(またはWindows管理者権限)が必要です。
import socket
import struct
RAWソケットを作成(Linux環境、root権限が必要)
IPPROTO_RAW で生のIPパケットを受信
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_TCP)
print("パケット受信待機中(TCPのみキャプチャ)...")
1パケット受信
raw_data, addr = s.recvfrom(65535)
IPヘッダ(最初の20バイト)を解析
iph = struct.unpack('>BBHHHBBH4s4s', raw_data[:20])
protocol = iph[7]
src_ip = socket.inet_ntoa(iph[8])
dst_ip = socket.inet_ntoa(iph[9])
print(f"送信元IPアドレス : {src_ip}")
print(f"宛先IPアドレス : {dst_ip}")
print(f"プロトコル番号 : {protocol}")
プロトコルの種類を判定
if protocol == 6:
print("プロトコル : TCP")
elif protocol == 17:
print("プロトコル : UDP")
elif protocol == 1:
print("プロトコル : ICMP")
else:
print(f"プロトコル : その他(番号 {protocol})")
出力結果:
パケット受信待機中(TCPのみキャプチャ)...
送信元IPアドレス : 203.0.113.42
宛先IPアドレス : 192.168.10.5
プロトコル番号 : 6
プロトコル : TCP
Wiresharkでのプロトコル番号確認方法
コードを使わなくても、WiresharkというGUIツールを使えばパケットのプロトコル番号を簡単に確認できます。WiresharkはオープンソースのGUIパケットアナライザで、ネットワーク上を流れるパケットをリアルタイムにキャプチャして表示する機能を持っています。
Wiresharkでパケットを選択し、「Internet Protocol Version 4」のセクションを展開すると「Protocol: TCP (6)」のように表示されます。フィルタ機能を使えば、`ip.proto == 6`と入力するだけでTCPパケットのみを絞り込むことも可能です。視覚的にプロトコル番号を確認したい場合には、Wiresharkが最も手軽な選択肢といえるでしょう。
まとめ
本記事では、「TCPのプロトコル番号とは?IPヘッダでの識別も!(プロトコル番号6:IP層:UDPは17:識別子:パケット解析など)」というテーマをもとに、TCPのプロトコル番号「6」の意味から、IPv4ヘッダの構造、UDPとの違い、Pythonを使ったパケット解析の実践まで幅広く解説してきました。
TCPのプロトコル番号は「6」、UDPは「17」というのは、ネットワークの基礎知識として必ず押さえておきたいポイントです。これらの番号はIPヘッダのProtocolフィールドに格納され、IP層が上位プロトコルを識別するための重要な識別子として機能しています。
Pythonのscapyやstructモジュール、あるいはWiresharkを活用すれば、実際のパケットの中からプロトコル番号を取り出して確認することも難しくありません。理論だけでなく、こうした実践的なアプローチを組み合わせることで、ネットワークの仕組みへの理解がより一層深まっていくでしょう。
プロトコル番号の知識は、ファイアウォールの設定やトラブルシューティング、セキュリティ対策にも直結する実用的なものです。ぜひ今回の内容を活かして、ネットワーク技術のさらなる習得に役立ててみてください。
Sonnet 4.6