旧 v6 の実装 ℃ MPv6 の実装 9 月に、フランスの ETSI (European Telecommu- nications Standards lnstitute) による lPv6 相互接続 イベントか夫施されました 1 。 KAME プロジェクトとし ては参加していませんが、プロジェクトのメンバーがそれ ぞれの立場で KAME の実装や KAME を採用した製品 を持ち込み、他の参加企業と相互接続テストをおこないま した。相接続テストは、他の実装者と妾対面できる貴 重な場です。また、テストの内容をみていると、 IPv6 の 現在の状態をある程度窺い知ることができます。 最初に気づくのは、アジア圏からの参加組織の比率の高 さです。米国などのインターネットも匯国は、しつはそ れほど IPv6 の必要性を感していないといわれています。 IPv6 のもっとも大きな特徴は広大なアドレス空間ですが、 IP アドレスの本曷に悩んでいるのは、おもにあとからイ ンターネットに参加した国々です。したがって、インター ネットの世界では後発にあたる ( 日本を含む ) アジア勢が IPv6 に大きな期待を寄せるのは自然なことといえます。 次に、相接続テストの内容が、 IPv6 の基本や IPv6 による糸響各制御の基本から、徐々にそれ以外の高度なもの に移りつつあることも感しとれます。すなわち、 IPv6 自 体や基本的な糸各制御については、はは動作することカ蝣 信できてきたということです。今回の相互接続イベントで は、 MobiIe IPv6 や IPsec 、マルチキャストなどが最先 端の話題としてとりあげられていました。 このように、技貧勺には着実に成熟しつつある IPv6 で すが、普及への道のりはいまだ遠いようです。 IPv6 国である日本にいてさえ、まだ IPv6 を身近に感じるこ とはできません。とはいえ、技術の成熟は確実に普及への 1 http://www.etsi.org/plugtests/ UNIX MAGAZINE 2002.12 ードルを低くしてくれます。ーヨ殳の利用者がもっと手軽 ノ、 に IPv6 を使える環境か整えば、彼らか経験したことのな い新しい ( あるいは、、、古き良き " と言い換えてもよいか もしれません ) インターネットの利用法とともに IPv6 が 広まると期侍しています。 IPv6 と ICMP IPv4 における ICMP は、 IPv4 の運用にかかわる実質 的な彳難リをはとんど担っていませんでした。これに対し、 IPv6 用の ICMP である ICMPv6[1, 2 ] は、 IPv6 を実 現するために必須の機能を提供します。これまでに解説し たものも含め、 ICMPv6 ( 以後たんに ICMP と表記 ) を利用して : 滝見されているおもな機能を以下に挙げます。 近ド蝌架索 (Neighbor Discovery) [ 3 ] ーリンク上のノードのデータリンク層アドレス角夬 ーリンク上のルータの発見 経路 MTU 探索 (Path MTU Discovery) [ 4 ] 通イ滸目手への糸響各」 : の最小 MTU (Maximum Trans- mission Unit : 最大転送ユニット ) の発見 ・マルチキャスト受信者孑架索 (Multicast Listener Dis- covery) [ 5 ] リンク上で受信されているマルチキャスト・アドレスの ICMP では、機能ごとに個別のタイフ。番号か割り当て られています。ただし、どの機能であれ基本は ICMP で すから、受信したバケットはまず ICMP として処理さ れます。 ICMP に共通の処理を終えると、それぞれの機 能の処理に続きます。たとえは、以前に解説した近隣要請 71
連載 / IPv6 の実装 - の ます。 ip6c ー 0 仕は、エラーの原因となった IP バケットの IP ヘッダから最後尾のヘッダまでのオフセットです。た とえは、もとの IP バケットの上位層プロトコルが TCP であれば、 TCP ヘッダの : 頁を指します。 ip6c-src は、 工ラーの原因となった IP バケットの始点アドレスです。 ip6c-cmdarg には、通知番号に応じた伺属情報を↑翻タし ¬ i f ymtu ; ip6cp ・ ip6c-cmdarg = (void * ) icmp6—mtu) ; notifymtu ntOhI (icmp6—> ICMP6_PACKET_TOO_BIG) { if (icmp6type ます。 ip6c-nxt は最後尾ヘッダのプロトコル番号です。 1358 1354 1353 1352 -P((int, 1360 ctlfunc = (void ( * ) で MTU の値を参照できるようにします。 icmp6-mtu にオ褓内されているので、 ip6c-cmdarg 経由 値を送信者に通知しなければなりません。 MTU の値は たインターフェイスの MTU が含まれているため、その す。 ICMP6-PACKET-TOO-BIG にはエラーが発生し ータが ICMP6-PACKET-TOO-BIG を返送してきま ス MTU よりも大きかった場合は、転送に失敗したル 送信したバケットのサイズカ響各一ヒのインターフェイ 1361 struct sockaddr * , void * ) ) ) (inet6sw Cip6—protox [nxt] ] . pr-ctlinput) ; 工ラーメッセージの通知には、プロトコルごとに定義さ れている制御関数を使います。制御関数へのポインタは、 ip6protosw オ冓造イ本の pr-ctlinput に成疋されているの で、 nxt にオ褓内されているプロトコル番号と ip6protosw 構造体の配列である inet6sw から、対応する制御関数の ポインタを取得します ( 1 , 360 ~ 1 , 361 行目 ) 。 1362 1363 1364 1365 1366 } if (ctlfunc) { (void) (*ctlfunc) (code, (struct sockaddr * ) &icmp6dst , &ip6cp) ; 1367 return ( 0 ) ; 最後に、制御関数に通知番号と通知メッセージ、および 工ラーの原因となった IP バケットの終点アドレスを渡 84 し、 ICMP での通知処理の完了です。以後の処理は各プ ロトコルごとに実行されます。 ☆ [ 赭文献 ] 索のコードを角見します。 次回は ICMPv6 の実装の続きとして、糸各 MTU 探 ( しま・けいいち IIJ) UNIX MAGAZINE 2002.12 December 1998 ProtocoI, ver 0 れ 6 は P の S 〃 ec ca 0 れ , RFC2460 [ 6 ] Stephen E. Deering and Robert M. Hinden, lnternet IPv6, RFC2710 , October 1999 Haberman, MuIticast 力なれ et 、 D な co ゼ e 型イん D 丿扣 7 、 [ 5 ] Stephen E. Deering, William C. Fenner and Brian 1981 , August 1996 gul, P 観ん MTU D co ゼ e 型ル 7 、 IP et 、 s れ 6 , RFC [ 4 ] Jack McCann, Stephen E. Deering and Jeffrey Mo- の , RFC2461 , December 1998 Simpson, Ⅳ e 朝んわ 07 、 Discovery ル 7 、 IP Version 6 (IP [ 3 ] Thomas Narten, Erik Nordmark and William Allen icmp-v3, November 2001 col 佐れ 6 は P ゼの S ec 第 c 観れ , draft-ietf-ipngwg- Message PT 、 otocol は C イ P のル 7 、 the lnternet 、 0t0- [ 2 ] AIex Conta and Stephen Deering, 加カ er れ Co れな祝 cember 1998 tocol V 谷、 s れ 6 は P の S ec c 佖 0 れ , RFC2463 , De- Message Protocol (ICMPv のル 7 、 the lntet 、 net PT 、 0 ー [ 1 ] AIex Conta and Stephen Deering, 石 et 、〃 e カ Control
連載 / IPv6 の実装ーの とに異なる情報が入ります。 最初に、 ICMP バケットの正当性をチェックします。 561 562 565 566 572 ip6 = mtod(m , struct ip6-hdr * ) ; if (icmp61en く sizeof (struct icmp6—hdr)) goto freeit ; (struct icmp6-hdr * ) ( (caddr-t) ip6 + off); icmp6 icmp6-input() の引数として渡された ICMP バケット 長が、仕様上の ICMP バケットヘッダ長よりも小さい場 合には、処理を続けることかできません ( 565 行目 ) 。ヘッ ダ長に間題がなければ、 572 行目で ICMP バケットの先 頭をポインタ変数 icmp6 に設疋し、以後は変数 icmp6 経由でバケット内部のデータにアクセスできるようにしま icmp6—>icmp6—code ; 581 code す。 583 ~ 590 行目は ICMP バケットのチェックサムの検 出てきます。 ド番号をコピーします。タイフ。番号ごとの処理はのちほど 決まります。 code に、受信した ICMP バケットのコー ICMP バケットの内容は、タイフ。番号とコード番号で 算です。 583 if ((sum in6—cksum (m, IPPROTO ー ICMPV6 , off , icmp61en) ) ! = 0 ) { goto freeit ; 589 590 } チェックサムが合わない場合、バケットカ臻れているか 改竄のおそれがあるため破棄します。 622 623 624 625 626 627 if (ip6—getpktaddrs (), &srcO, &dstO)) { SI'C dst *srco; *dstO; m—freem(m) ; return (IPPROTO_DONE) ; 受信したバケットの始点アドレスと終点アドレスを参照 する際に、バケット自体の始点アドレスおよひ終点アドレ ス・フィールドを参照してはいけません。これは、アドレ スがグローバル・スコープではなかったときに、正しいス コープ情報を取得することができないからです。スコープ 情報付きの始点 / 終点アドレスは、 ip6-input() が IP パ 74 ケットを処理する過程で補助 mbuf に↑タされています。 622 ~ 627 行目では、補助 mbuf の始点 / 終点アドレスを 取り出します。 こまでがすべての ICMP バケットに共通の処理で す。以後、タイフ。番号に応した個別処理に進みます。 タイプ番号ごとの里 635 行目からがタイフ。番号別の処理になります。 635 switch (icmp6—>icmp6—type) { 以前解説したとおり、近謝架索など一部のプロトコル は、 icmp6änput() から呼び出されることを想定した独 自の入力関数を備えています。タイフ。番号が近ド爾架索里 のものであれば、単純に対応する入力関数か呼び出されま す。今回は、以下の ICMP タイフ。の処理について解説し ます。 ・ ICMP6-DST-UNREACH (Destination Unreach- able) バケットを宛先に届けることかできなかった。 ・ ICMP6-PACKET-TOO-BIG (Packet T00 Big) バケットがデータリンク層 MTU のサイズを超えた。 ・ ICMP6-TIME-EXCEEDED (Time Exceeded) 時間切れになった。 ・ ICMP6-PARAM-PROB (Parameter Problem) バケットの内部データに不備があった。 ・ ICMP6-ECHO-REQUEST (Echo Request) 工コー要求。 ・ ICMP6-ECHO-REPLY (Echo Reply) ェコー。 ICMP6-DST UNREACH の里 ICMP6-DST-UNREACH (Destination Unreach- able) は、バケットが最終目的地まで届かなかった場合に j されてくるエラーメッセージです。酉当医失敗の理由と して、以下のコードか定義されています。 ・ ICMP6-DST-UNREACH-NOROUTE ()o route to destination 終点ノード , 、、の経路情報がない。 UNIX MAGAZINE 2002.12
連載 / 旧 v6 の実装ーの 図 1 ip6-init() からの抜粋 (ip6-input. c) 258 259 260 261 262 263 264 265 266 267 pr = (struct ip6protosw * ) pffindproto (PF_INET6 , if (pr = 0 ) panic("ip6-init") ; for (i = 0 ; i く IPPROTO_MAX; i + + ) ip6—protox [i] pr ー inet6sw; IPPROTO_RAW, SOCK_RAW) ; for (pr (struct ip6protosw * ) inet6domain. dom_protosw; ip6—protox [pr—>pr—protocol] = pr ー inet6sw; pr—>pr—protocol & & pr->pr-protocol ! = IPPROTO RAW) PF_INET6 & & if (pr—>pr—domain—>dom—family pr く (struct ip6protosw * ) inet6domain. dom_protoswNPROTOSW ; pr + + ) (Neighbor Solicitation) は、ます IP バケットとして処 理されたあと、 ICMP の処理部に渡されます。 ICMP の 処理のなかでは、すべての ICMP バケットに共通の処理 を終えてから、近隣要請を示すタイプ番号に従ってその入 カ処理をおこなう nd6-ns-input() に進みます。 今回参照するファイル 今回は ICMP の入力処理を解説します。参照するファ イルは以下のとおりです。 ・ kame/sys/netinet/ip6. h ( 改訂番号 1.35 ) ・ kame/sys/netinet6/icmp6. h ( 改訂番号 1.76 ) ・ kame/sys/netinet6/icmp6. c ( 改言丁番号 1.325 ) ・ kame/sys/netinet6/ip6-input. c 攵言丁番号 1.295 ) ・ kame/sys/netinet6/ip6protosw. h ( こ攵丁番号 1.28 ) ICMP の入力里 ICMP バケットの処理は icmp6-input ( ) で実装され ています。以 - ド、ファイル名を明記していないコードは 537 icmp6—input ()p , offp , proto) 536 int icmp6. c のものです。 対象となるプロトコルがカーネルにとって未知のものだっ たときです。 ICMP にかぎらす、 KAME では IPv6 ロ里するプロ トコルの入力関数の型が次のように定義されています ( た だし、 IP バケットの入力関数である ip6-input() は例外 ip6protosw. h 128 int 129 (*pr—input) P( (struct mbuf * * , int * , int)) ; ip6-input() を除き、すべての入力関数は 1 : 記の型であ る必要があります。ここで、 ip6-input() が上位層へッダ を処理する部分を思い出してみましよう。 ip6 -input. c 1088 while (nxt ! = 1150 } 1149 nxt IPPROTO_DONE) { (*inet6sw Cip6-protox [nxt] ] ・ pr—input) (tm, &off, nxt) ; 538 539 540 { struct mbuf **mp ; int *offp , proto ; icmp6-input() は 3 つの引数をもちます。 mp は受信 した IP バケットへの mbuf ホインタ、 o 仕 p は IP バケッ トのう t:j 頁から ICMP'S ケットのう巨頁までのオフセットで す。 proto は処理中のプロトコル番号で、 ICMPv6 の場 合は 58 です。実際には、 proto の値は icmp6-input() で は利用されません。この第 3 引数か意味をもつのは、処理 72 inet6sw は ip6protosw オ冓 j 匿一本の酉リです。 ip6proto- sw 構造体には、 IPv6 関連のプロトコルの情幸肋財内され ます。たとえば、各プロトコルのバケットを処理するとき に呼び出す入力関数、プロトコルのフラ久制御関数、タ イマー関数などです。 ip6protosw 構造体のメンバー変数 の 1 つである pr-input には、プロトコルの入力関数への ポインタか設定されます。 inet6sw は ip6-init() で初期 化されており、その時点で各プロトコルに適した入力関数 か設定されるイ督はみになっています。図 1 のコードは ip6 ー init ( ) からの抜枠です。 ます、 258 行目で IPv6 (PF-INET6) の RAW プロ トコル (IPPROTORAW) を処理するための情報を検 索します。カーネルは ip6protosw 構造体の配列を一尉寺 UNIX MAGAZINE 2002.12
連載 / lPv6 の実装ー・ 800 801 } noff ((caddr—t)nip6 + off) ; off; ICMP6-ECHO REPLY 用の IP ヘッダのアドレス を nip6 に、 ICMP6 ヘッダのアドレスを nicmp6 に設 定し、 IP ヘッダのう巨頁から ICMP ヘッダの知直までの オフセットを n 。仕に設定します。 知されます。 1095 deliver: 1100 1099 1098 1096 break ; if (icmp6—notify—error(m, off , icmp61en, code)) { return (IPPROTO_DONE) ; 802 803 804 807 808 nicmp6->icmp6-type = ICMP6_ECHO REPLY; if (n) { nicmp6—>icmp6—code icmp6—ref1ect ( Ⅱ , noff) ; nicmp6 は、 lCMP6-ECHO-REPLY に使う ICMP バケットの先頭を指しています。この点では ICMP6- ECHO-REQUEST のコピーになっているので、 802 行 目でタイフ。番号を ICMP6-ECHO-REPLY に修正し、 念のためにコード番号を初期化しておきます。そして、 804 ~ 808 行目で icmp6-reflect() を使ってノヾケットを 返送します。 icmp6-reflect() は、第 1 引数で渡された mbuf にイ褓内されている IP バケットの始点アドレスと終 点アドレスを交換し、再出力する関数です。 goto freeit; if ( ! m) れば、 810 行目で icmp6-input() の処理を終了します。 ローカルソケットに酉占するためのコピーか存在しなけ break; 811 810 809 813 case ICMP6—ECHO—REPLY : す。 一方、 ICMP6-ECHO-REPLY の受信処理は単純で 通知か成功すると、 1 , 100 行目の break で 635 行目の switch 文を抜けます。なんらかの原因で通知に失敗した 場合は、 1 , 098 行目で処理を中断します。なお、 break で switch 文を抜けたあと、 ICMP バケットはその後ろに続 くコードで RAW プロトコル・ソケット , 当医されます。 前述したとおり、待ち受けている RAW プロトコル・ソ ケットか存在しない場合は、 ICMP バケットはどこへも 酉当されすに捨てられます。 1121 static int icmp6—notify—error(), 0ff , icmp61en, code) 1122 icmp6-notify-error() は 4 つの引数をもちます。 m 、 。仕は icmp6-input() の引数と同じもので、それぞれ ICMP バケットを含む IP バケットへの mbuf ポインタ、 IP バケットの : 麪頁から ICMP バケットのう頁へ、のオフ セットを示します。 icmp61en は IP'S ケットの長さから ICMP バケットの : 頁までのオフセットを引いた、純枠 な ICMP バケットの長さです。 code は、 icmp6-input() のなかで ICMP のタイプ番号とコード番号に応して変換 された制御堺号です。 1131 if (icmp61en く sizeof(struct icmp6—hdr) + 1133 sizeof (struct ip6—hdr) ) { goto freeit ; 815 816 817 if (code ! = 0 ) goto badcode ; break ; 工ラーメッセージ情報の配送 有効な ICMP のエラーメッセージを受信すると、 de- liver へ処理か夥り、 icmp6-notify-error() で送信者に通 受信した ICMP バケットのコード番号をチェックし、 そのままローカルソケット , 当医します。コード番号はと くに定義されておらす、 0 で初期化されていなけれはなり ません。 UNIX MAGAZINE 2002 ユ 2 1134 } 最初に、バケットの正当性をチェックします。受信し た ICMP の情報を送信者に酉当するためには、エラーを 発生させたバケットの送信者を調べなけれはなりません。 ICMP 工ラーメッセージでは、 ICMP ヘッダの直彳刻ンヾ ケットサイズカ滸すかぎり、エラーの原因となった IP パ ケットかオ褓内されています。 1 , 131 ~ 1 , 134 行目では、受 信した ICMP バケットのサイズが、すくなくともエラー の原因となった IP バケットのヘッタ部分を含んでいるか どうかを石忍します。原因となる IP バケットかオ内され ていない場合は、送信者にエラーを醋医することができま せん。 1 , 133 行目で処理を中断します。 79
ICMP6-PACKET-TOO-BIG のコード番号は定義 されていません。 0 で初期化することが仕様で定められて います。 0 以外のコード番号をもつ ICMP6-PACKET- TOO-BIG メッセージは、 676 行目で badcode に処理 を移します。 ICMP6-PACKET-TOO-BIG の目的は、終点アドレ スへ向かう経路の最小 MTU を発見し、バケットの分割 が発生しないバケットサイズで送信することです。最小 MTU の通知は deliver 以後に呼び出される icmp6-no- tify-error() て処理されます。 ICMP6 TIME-EXCEEDED の処里 ICMP6-TIME-EXCEEDED (Time Exceeded) は 以下のいすれかの間題が発生したときに生成されるエラー メッセージです。 ・指定されたホップリミットを超えた。 ・分割バケットが 60 秒以内 ( RFC2460 [ (]) に再構築で きなかった。 どちらの原因によって ICMP6-TIME-EXCEEDED が発生したかは、コード番号から判別できます。それぞ れ ICMP6-TIME-EXCEED-TRANSIT と ICMP6_ TIME-EXCEED-REASSEMBLY というコードカ轄リ り当てられています。 688 case ICMP6_TIME_EXCEEDED : 連載 / IPv6 の実装ーの ICMP6-PACKET TOO BIG の処理 ICMP6-PACKET-TOO-BIG (Packet Too Big) は、ルータがバケットを転送する際に、転送先インター フェイスの MTU か転送したいバケットよりも小さかっ たときに生成されます。 673 case ICMP6_PACKET_TOO_BIG : case ICMP6_TIME_EXCEED_TRANSIT : 693 694 695 696 697 698 699 700 break; case ICMP6-TIME_EXCEED_REASSEMBLY : code = PRC_TIMXCEED_REASS ; break; default : goto badcode ; goto deliver ; 675 676 680 686 goto deliver; if (code ! ま 0 ) goto badcode ; code = PRC_MSGSIZE ; 688 ~ 700 行目で、受信したコード番号を制御番号に変 換し、通知します。 ・ ICMP6-TIME-EXCEED-TRANSIT → PRC-TIMXCEED-INTRANS 転送の途中でホップリミットが 0 になった。 ・ ICMP6-TIME-EXCEED-REASSEMBLY → PRC-TIMXCEED-REASS 断片化バケットの再構築が 60 秒以内に完了しなかっ 他のメッセージと同様に、イ兼で定義されていないコー ド番号は badcode 以下て処理されます。 ICMP6-PARAM-PROB の里 ICMP6-PARAM-PROB (Parameter Problem) は バケットヘッダの処理中に、処理できないへッダがみつか ったときに生成されます。問題を発見した場所に応して、 case ICMP6_PARAM_PROB : 702 します。 702 ~ 721 行目でコード番号を制徊堺号に変換し、酉古医 未知の IPv6 オプションを受信した。 ・ ICMP6-PARAMPROB-OPTION IPv6 ヘッダ / 拡リをッタ情報に不備があった。 ・ ICMP6-PARAMPROB-HEADER 未知の Next Header 値を受信した。 ・ ICMP6-PARAMPROB-NEXTHEADER 次の 3 つのコードか定義されています。 710 711 712 713 714 715 716 switch (code) { case case 690 691 692 76 switch (code) { code PRC_TIMXCEED_INTRANS ; 工 CMP6 _PARAMPROB _NEXTHEADER : code PRC_UNREACH_PROTOCOL ; break; I CMP6 _PARAMPROB—HEADER : ICMP6_PARAMPROB_OPTION : code = PRC_PARAMPROB ; UNIX MAGAZINE 2002 ユ 2
連載 / IPv6 の実装ー・ ・ ICMP6-DST-UNREACH-ADMIN (communica- tion with destination administratively prohib- ited) ネットワーク管理者によって送信か禁止されている。 ・ ICMP6-DST-UNREACH-BEYONDSCOPE (be- yond scope 0f source address) アドレススコーフ。の範囲を越えた。 ・ ICMP6-DST-UNREACH-ADDR (address ur ト reachable) 上言己外の理由て終点ノードへの醋医が失敗した。 ・ ICMP6-DST-UNREACH-NOPORT (port un- reachable) 終点ノード上の終点ポート宛のバケットを受信するプロ セスか有在しない。 ICMP6-DST-UNREACH を受信したら、エラーの 原因となった IP?NO ケットの送信者にエラーの内容を伝え なけれはなりません。その処理は以下のようになります。 636 case ICMP6_DST UNREACH : ・ ICMP6-DST-UNREACH-ADDR → PRC-HOSTDEAD 終点ノードか起動していない。 ・ ICMP6-DST-UNREACH-BEYONDSCOPE → PRC-UNREACH-NET ネットワークへの糸習各が存在しない。 ・ ICMP6-DST-UNREACH-NOPORT → PRC-UNREACH-PORT 不正なポート番号。 正しく変換できたら、 671 行目で送信者へ ICMP の情 報を通知します。 イで定義されていないコード番号を受信した場合、カ ーネル内部ではこれ以上の処理はできません。このときは badcode に処理を移します ( 668 ~ 669 行目 ) 。具イ純勺な 処理は以下のようになります。 644 645 646 647 668 669 670 671 switch (code) { case ICMP6_DST_UNREACH_NOROUTE : code = PRC_UNREACH_NET ; break ; default : goto badcode ; goto deliver ; 1102 1104 1109 1112 1114 badcode : break ; icmp6—rip6—input(&m, *offp, &src , &dst) ; return IPPROTO_DONE ; ます、 ICMP6-DST-UNREACH と一緒に返送され てきたコード番号を、プロトコルに依存しない制御番号 に変換します。たとえば、 ICMP6-DST-UNREACH- NOROUTE であれは、ネットワークへの経路か存在し ないことを通知する PRC-UNREACH-NET に変換し ます ( 646 行目 ) 。はかの 4 つのコード番号も、同様の手 順て対応づけます ( 648 ~ 667 行目。コードは省略してい ます ) 。 UNIX MAGAZINE 2002.12 未知のプロトコル番号。 PRC-UNREACH-PROTOCOL ・ ICMP6-DST-UNREACH-ADMIN → ネットワークへの経路か有しない。 PRC-UNREACH-NET ・ ICMP6-DST-UNREACH-NOROUTE → icmp6-rip6-input() を使って、対応する RAW プロト コル・ソケットに受信したバケットの内容を通知し ( 1 , 112 行目 ) 、 ICMP の処理を終了します。 ICMP バケットを 受信するソケットが作られていなけれは、 ICMP の情報 は失われます。 一方、信で定義されているコード番号を受信したとき は、 deliver 以下で処理カ引米続されます。 1095 deliver : 1096 1098 1099 if (icmp6—notify-error(m, off , icmp61en, code)) { return (IPPROTO_DONE) ; icmp6-notify-error() は、エラーの原因となった送信 者 ( 具体的には PCB (Protocol Control Block : プロ トコル制御プロック ) ) に制御堺号を通知します。 75
717 718 719 720 721 連載 / lPv6 の実装ー・ break ; goto deliver; goto badcode ; default : ピーに失敗したら deliverecho に進み、 ICMP6-ECHO- REPLY の返信処理を続けます。この場合は、ローカルで ICMP ソケットを開いているプロセスへの配送をあきら めます ( 734 ~ 736 行目 ) 。 if ( (n—>m-flags & M_EXT) ! = 0 Ⅱ 743 変換の対応は以下のようになります。 ・ ICMP6-PARAMPROB-NEXTHEADER → PRC-UNREACH-PROTOCOL 未知のプロトコル番号。 ・ ICMP6-PARAMPROB-HEADER および ICMP6 -PARAMPROB-OPTION → PRC-PARAMPROB 処理できないへッダ。 他のメッセージと同様に、信にないコード番号を受信 した場合は badcode へ進みます。 ICMP6-ECHO-REQUEST/ICMP6- ECHO-REPLY の里 ICMP6-ECHO-REQUEST (Echo Request) およ び ICMP6-ECHO-REPLY (Echo Reply) はおもにネ ットワークの導通テストに利用されるもので、 IPv4 のこ 723 case ICMP6—ECHO_REQUEST : ろからお馴染みのメッセージです。 744 n—>m 1en く off + sizeof (struct icmp6—hdr)) { 725 726 if (code ! = 0 ) goto badcode ; ICMP6-ECHO-REQUEST にコード番号は定義さ れていません。 0 で初期化することが定められています。 if ()n = m—copym(), 0 , M—COPYALL, 732 734 735 736 M-DONTWAIT) ) = = NULL) { Ⅱ = m; m = NULL ; goto deliverecho ; 737 } 732 行目で、受信した ICMP6-ECHO-REQUEST メッセージをコピーしています。コピーしたメッセージ 11 は、 ICMP6-ECHO-REPLY を返すために利用します。 もとのメッセージ m は、 ICMP のデータを受信するため にソケットを開いて待機しているプロセスへの酉占幻こ利用 します。 m-copym() に M-COPYALL を指定すること で、 mbuf の内部フラグまて含めたコピーを作ります。コ UNIX MAGAZINE 2002 ユ 2 n には受信した ICMP6-ECHO-REQUEST メッセ ージのコピーかオ褓内されています。 mbuf をコピーして利 用する際に注意しなけれはならないのは、 mbuf のデータ ー尉寺のガ去です。 mbuf がデータ尉寺するガ去は 2 不頁 あります。 1 つ目は mbuf 構造体のなかにデータをもつ方 法です。もう 1 つは、 mbuf 構造体のなかではなく、別 のメモリ領域にデータを確保するガ去です。後者の場合、 mbuf はデータかオ褓内されたメモリ領域を指し示すポイン タのみをもちます。 後者の方法では、 m-copym() で mbuf をコピーする と、別に確保されたメモリ領域はコピーされす、ポインタ のみがコピーされます。つまり、メモリ領域は 2 つの異な る mbuf から参照されることになります。このとき、どち らかの mbuf でデータを操作すると、もう一方の mbuf に も景グをおよほ、してしまいます。 mbuf が外部にデータをもつ場合、 m-flags に M-EXT が設定されています。 743 行目は、そのチェックをおこ なっています。 744 行目では、 IP ヘッダから ICMP ヘッダまてが 1 つの mbuf に含まれているかどうかをチェックしていま す。 ICMP6-ECHO-REPLY は、タイプ番号とコード 番号を除けは、ほは、 ICMP6-ECHO-REQUEST と同 じ形式です。一からバケットを作るよりも、コピーした IP'S ケットのヘッダを修正するほうか簡単です。そこで、 あとでポインタを使って IP バケットのヘッダフィールド 作できるように、 IP ヘッダから ICMP ヘッダ までが連続してメモリ上に配置してあるかどうかを石忍し ているわけです。 コピーしたバケット n がどちらかの条件を満たしてい る場合には、コピーしたものをそのままの状態て利用する ことはできません。 IP ヘッダと ICMP ヘッダをオ褓内で きる大きさの mbuf を新たに作り、 n の前に追加する必要 があります。 77
745 746 752 754 755 756 連載 / IPv6 の実装ーの break; m—freem(nO) ; if (maxlen > = MCLBYTES) { sizeof (*nicmp6) ; const int maxlen = sizeof (*nip6) 十 struct mbuf *nO = Ⅱ ; 784 785 sizeof (struct ip6—hdr) ; noff n—>m_len = noff + sizeof (struct icmp6_hdr) ; maxlen は、 IP ヘッダと ICMP ヘッダの合引 , 値てす。 maxlen が mbuf クラスタの大きさである MCLBYTES を超える場合は、 ICMP6ÆCH(M{EPLY 窈医をあき らめます。 i386 アーキテクチャでは MCLBYTES の値 は 2048 ですから、通常はここで間題になることはないで 758 if ( Ⅱ & & maxlen > = MHLEN) { 757 MGETHDR(n , M_DONTWAIT, nO—>m_type) ; しよう。 759 760 761 762 763 764 } MCLGET (n , M_DONTWAIT) ; if ( (n—>m—flags & M—EXT) m—free(n) ; n = NULL ; 757 行目で IP ヘッダと ICMP ヘッダをコピーするた めの mbuf を石呆します。 758 ~ 764 行目は一定の大きさ の mbuf を確保するときのお決まりのイ料去です。 MGET- HDR() で取得した mbuf の大きさか要求を満たさなけれ ば、 MCLGET() で mbuf クラスタを確保します。 765 if ( Ⅱ = NULL) { 767 768 769 770 m—freem(nO) ; Ⅱ = m; m = NULL ; got0 deliverecho ; 771 } mbuf の確保に失敗したら、ローカル酉当用の mbuf rn を n の代わりに利用し、ローカル酉占医をあきらめます。 775 780 781 782 783 M_COPY_PKTHDR(n , Ⅱ 0 ) ; nip6 = mtod(), struct ip6—hdr * ) ; bcopy(ip6, nip6, sizeof(struct ip6—hdr)) ; (struct icmp6—hdr * ) (nip6 + 1 ) ; nicmp6 bcopy (icmp6 , nicmp6 , sizeof (struct icmp6—hdr)) ; そして、新たに確保した mbuf のデータ部の長さを設 定します。この mbuf には、直前にコピーした IP ヘッダ と ICMP ヘッダのみかオ褓内されています。 続いて、 m-pkthdr. len を調整します。 791 n—>m—pkthdr. Ien + = noff + sizeof(struct icmp6—hdr) ; (off 十 792 n—>m—pkthdr. 1en sizeof(struct icmp6—hdr) ) ; m 」 en が 1 つの mbuf の長さを示すのに対し、 m-pkt- hdr. len は複数の mbuf から構成された IP バケット全体 の長さを示します。 IP ヘッダと ICMP ヘッダを新たに 作ったので、ます 791 行目で追加したヘッダの長さを加 えます。次に、このあとでもとの IP バケットの : 頁から ICMP ヘッダまでの部分を削除するので、 792 行目で削 除する長さを引きます。 793 m—adj ()O , off + sizeof (struct icmp6—hdr) ) ; 794 n—>m_next = Ⅱ 0 ; 795 nO—>m—fIags & = -M_PKTHDR; 最後に、 793 行目でもとの IP バケットが収められてい る mbufnO から、 IP ノヾケットのク頁から ICMP ヘッダ までの領域を削り、 794 行目て新たに確保した mbufn の 後ろに連結します。 110 はこの時点でバケットのう頁では なくなるので、バケットの先頭を示す MNKTHDR フ ラグを落とします。 796 } else { 743 ~ 744 行目の条件に適合しない、すなわち ICMP6 -ECHO-REPLY 用にコピーした mbuf が mbuf クラ スタではなく、かっ先頭の mbuf にバケットの先頭か ら ICMP ヘッダまでが含まれている場合は、コピーした mbuf を使って ICMP6ÆCHO-REPLY を送ります。 797 deliverecho : 743 ~ 744 行目の条件に適合した場合でも、 ICMP6- ECH(M{EPLY 用の mbuf を確保する過程でエラーが 発生し、ローカルソケットへの配送をあきらめたときは deliverecho に処理カ夥ってきます。 新たに石呆した mbuf に、受信した ICMP6-ECHO- REQUEST の IP ヘッタ著にと ICMP ヘッタにをコピー します。 78 798 799 nip6 = mtod(n , struct ip6—hdr * ) ; (struct icmp6—hdr * ) n1cmp6 UNIX MAGAZINE 2002.12
連載 / 旧 v6 の実装ーの 図 5 スコ ープ情報の復元 1316 1317 1318 1319 1323 1324 1307 if if (in6—addr2zoneid(m—>m—pkthdr. rcvif , &icmp6dst. sin6_addr , &icmp6dst . sin6-scope-id) ) goto freeit; (in6—embedscope (&icmp6dst. sin6—addr , &icmp6dst) ) { goto freeit; (struct ip6—hdr * ) (icmp6 + 1 ) ; eip6 eip6 はエラーの原因となった IP バケットのう巨頁を指 します。すでに 1 , 148 行目で eip6 を設疋していますが、 ヘッダの連鎖を追う過程で mbuf を操作するため、 eip6 カ甘旨している値か無効になっている可能性があります。そ if (finaldst = = NULL) icmp6dst . sin6—fami1y = AF—INET6 ; sockaddr—in6) ; 1cmp6dst . sin6—1en = sizeof(struct bzero(&icmp6dst , sizeof (icmp6dst) ) ; こで、 1 , 307 行目で eip6 を正しい値に更新します。 上最善の手を尽すしかありません。多くの場合、 1 , 316 ~ 1 , 324 行目で正しいスコープ情報か復元されると考えら れます。 1 , 326 ~ 1 , 339 行目は、同様の操作を始点アドレスに対 して実行します。ほとんど同しコードなので説明は省略し ます。 1340 *. sin6—f10winf0 1309 1310 1311 1312 1313 1314 1315 1341 (eip6—>ip6—f10w & IPV6_FLOWLABEL_MASK) ; else icmp6dst . sin6—addr = *finaldst ; icmp6dst . sin6—addr = eip6—> ip6—dst ; 1 , 340 ~ 1 , 341 行目でフローラベル情報を彳第こし、アド レス情報の彳第己は完了です。 続いて、エラーを通知するために利用する通知メッセー ジの生成に移ります。 icmp6dst にエラーの原因となった IP ノヾケットの終点 アドレスを設疋します。 finaldst は糸響各制御ヘッダが存 し、すべての中継ノードの処理か完了していないときのみ NULL 以外の値をもちます。この場合、 finaldst か終点 アドレスを指しているので、 icmp6dst に値をコピーしま す ( 1 , 312 ~ 1 , 313 行目 ) 。 finaldst が NULL であれは、 ェラーの原因となった IP バケットの終点アドレスをその まま利用します ( 1 , 315 行目 ) 。 1 , 316 ~ 1 , 324 行目 ( 図 5 ) はスコープ情報の復元てす。 ICMP'S ケットを受信したインターフェイスからスコー フ情報を彳己します。しかしながら、こ作はかならす ーこで彳第己しようとしているの しも正確ではありません。 は、エラーの原因となった IP バケットのアドレスに関 するスコーフ請報です。めったにない例ですが、インター フェイスが 2 つ以ーヒある場合、成疋によっては IP バケッ トを出力したインターフェイスとは別のインターフェイス へ ICMP 工ラーメッセージが届くこともあります。しか し、受信した ICMP バケットの末尾に含まれている IP バケットからは、どちらのインターフェイスへ出力したパ ケットなのかを復元する情幸ゞありません。手段がない以 UNIX MAGAZINE 2002.12 1345 1346 1347 1348 1349 1350 ip6cp ・ ip6c-m = m; ip6cp ・ ip6c—icmp6 1cmp6 ; ip6cp. ip6c-ip6 ip6cp. ip6c—0ff ip6cp ・ ip6c—src ip6cp. ip6c—nxt (struct ip6—hdr * ) (icmp6 + 1 ) ; eoff ; &icmp6src; nxt ; ip6cp は ip6ctlparam 構造イ本の変数で、 ICMP の工 ラーメッセージを送信者に通知するために使います。この 構造体の定義を以下に示します。 ip6protosw. h 107 struct ip6ct1param { 108 109 110 111 112 113 114 115 } ; struct mbuf *ip6c—m ; struct icmp6—hdr *ip6c—icmp6 ; struct ip6—hdr *ip6c—ip6 ; int ip6c—0ff ; struct sockaddr—in6 *ip6c—src ; void *ip6c-cmdarg ; u—int8_t ip6c_nxt ; ip6c-m は IP ヘッダを含む ICMP バケットです。 ip6c -icmp6 は ICMP バケットのう頁アドレス、 ip6c-ip6 は 工ラーの原因となった IP バケットのう頁アドレスを指し 83