はじめに
この連載では仮想的なLANを実装することにより、目に見えないTCP/IPプロトコル群を、手を動かして目で実際に確認しながら習得することを目的としています。ただし、TCP/IPの全てを解説するのは分量上不可能ですし、余計に読者を混乱させてしまいますので、筆者が重要と考えている部分だけ解説します。またサンプルコードや本記事に出てくるIPアドレスとMACアドレスについては架空のものであり、実際にそのIPアドレスを取得した組織などを意識したものではありません。あらかじめご了承ください。
今回はインターネット層のプロトコルとそれに関係するネットワークインタフェース層のプロトコルARPについて解説します。
ネットワーク層で使用するアドレス
前回通信相手を特定するためにMACアドレスを使用することを説明しました。しかし、インターネット層では、IPアドレスという名前の別のアドレスを使用します。わざわざそんなことをする理由は、そうすることで柔軟性が得られるからです。分かりにくいと思いますのでこれから具体的に説明します。
MACアドレスはNICなどの物理的なものに付与されているアドレスです。ですから、サーバーにこのアドレスを使用すると、NICを変更したらサーバにアクセスできなくなるので、その都度各種設定を変えねばなりません。機器を新しいものにしたり機器を移動したりする度に、関連しているソフトウェアの設定などを変えるのは大変な作業です。このような事態を防ぐためにインターネット層では別のアドレスを使用します。
このネットワーク層のアドレスのことをIP(Internet Protocol)アドレスと呼びます。IPアドレスを使用することにより、異なるネットワーク同士で通信を行えるようになります。次節でネットワーク間の通信について簡潔に説明します。
ネットワーク間の通信
ネットワーク間の通信は、専用の機器を使って、ネットワーク層独自のデータフォーマットのデータ(IPパケットと呼びます)をやり取りすることにより行います。このデータをやり取りする手順を定めたプロトコルをIP(Internet Protocol)と呼びます。
前回LAN内で端末同士が通信をする際に機器が必要だと説明しました。ネットワーク層でもそれは同じで、ルータと呼ばれる専用の機器を使用してネットワーク間で通信をします。また、通信の際にMACフレームをやり取りして通信を行うことも解説しました。こちらについても同様で、ネットワーク層独自のフォーマットのデータを、MACフレーム(Ethernetフレーム)内のユーザーデータとします。独自のデータフォーマットをMACフレーム内に埋め込む点に注意して下さい。この方式が連載1回目で解説したカプセル化です。
まずはデータフォーマットを紹介します。

| 部位 | 説明 |
| バージョン(4ビット) | IPのバージョンです。現時点では4か6が設定されます。 |
| ヘッダ長(4ビット) | IPヘッダの長さです。IPパケット内のデータの位置を探すために使用します。 |
| サービスタイプ(8ビット) | このデータの優先度を表します。 |
| パケット長(16ビット) | IPヘッダとデータを合わせた全体の長さを表します。 |
| 識別子(16ビット) | データを小分けにして送る際に使用するIDです。 |
| フラグ(3ビット) | IPデータの分割に関する情報です。 |
| フラグフラグメントオフセット(13ビット) | データを分割した際に設定する項目で、元のデータのどの部分にあったのかを表します。 |
| 生存時間(8ビット) | このパケットの寿命を表します。宛先が見つからない時に、データが永遠にネットワーク経路内をループするのを防ぐために使用します。 |
| プロトコル(8ビット) | ネットワーク層よりも一つ上のトランスポート層で使用するプロトコルを表します。 |
| ヘッダチェックサム(16ビット) | データが壊れていないかチェックする際に使用する値です。 |
| ソースアドレス(32ビット) | 送信元のIPアドレスです。 |
| ディスティネーションアドレス(32ビット) | 送信先のIPアドレスです。 |
| オプション | IPの拡張機能を使用するために指定するフィールドです。通常はオプションを指定しません。 |
| パディング(可変長) | IPパケットが32ビットの整数倍でなければ、このデータを埋めて調節します。 |
これでやり取りするデータの概要を解説しましたので、これからサンプルコードを用いてネットワーク間の通信について解説します。端末Aが違うネットワークに属する端末Bへデータを送りたいとします。IPでは、まず端末Bが同じネットワークに属していないかをルーティングテーブルで調べます。その結果同じネットワーク内に属していれば直接送り、違うネットワークに属している場合は、そのネットワークの場所を知っているルータに送ります。
/// <summary>
/// インターネット上へデータを送信します。
/// </summary>
/// <param name="data">送信するデータ</param>
public void Send ( Frame data )
{
//準備
if ( data.TypeOrLength == ( ushort ) EtherType.IP ) {
IPv4 packet = ( IPv4 ) ( ( MacFrame ) data ).UsertData;
//宛て先がLAN内に在るかどうかを判別して送信する
if ( packet.DestinationAddress.NetworkAddress ==
this.m_address.NetworkAddress ) {
this.m_lan.Send ( data );
} else {
//宛て先がLAN外なので、適切なルータを選んでインターネット上にデータを送信。
IPv4Address ruterAddress = null;
try {
ruterAddress = this.table.Find (
delegate ( RoutingElement element ) {
if ( element.Destination == packet.DestinationAddress )
return true;
else
return false;
}
).Gateway;
} catch ( NullReferenceException ) {
//指定されたホストに到達できないことを知らせる
this.icmp.SendDestinationUnreachableMessage ( ( MacFrame ) data );
}
this.Send ( ruterAddress, data );
}
} else {
this.m_lan.Send ( data );
}
}
''' <summary>
''' インターネット上へデータを送信します。
''' </summary>
''' <param name="data">送信するデータ</param>
Public Sub Send(ByVal data As Frame)
'準備
If data.TypeOrLength = CUShort(EtherType.IP) Then
Dim packet As IPv4 = CType((CType(data, MacFrame)).UsertData, IPv4)
'宛て先がLAN内に在るかどうかを判別して送信する
If packet.DestinationAddress.NetworkAddress = Me.m_address.NetworkAddress Then
Me.m_lan.Send(data)
Else
'宛て先がLAN外なので、適切なルータを選んでインターネット上にデータを送信。
Dim ruterAddress As IPv4Address = Nothing
ruterAddress = Me.SearchRoute(packet.DestinationAddress)
If ruterAddress Is Nothing Then
'指定されたホストに到達できないことを知らせる
Me.icmp.SendDestinationUnreachableMessage(CType(data, MacFrame))
Else
Me.Send(ruterAddress, data)
End If
End If
Else
Me.m_lan.Send(data)
End If
End Sub
icmp.SendDestinationUnreachableMessageについては後で説明します。
ネットワークとはどの範囲を指すのか気になる方がいると思いますが厳密な定義は複雑なので、現時点では同じLAN内に端末があれば同一ネットワーク、違えば異なるネットワークと考えて下さい。
これがネットワーク間の通信の概要です。これ以降の節では、ネットワーク通信を実現するための要素をより詳しく説明していきます。
