source: EcnlProtoTool/trunk/asp3_dcre/tinet/netinet6/nd6_nbr.c@ 321

Last change on this file since 321 was 321, checked in by coas-nagasima, 7 years ago

文字コードを設定

  • Property svn:eol-style set to native
  • Property svn:mime-type set to text/x-csrc;charset=UTF-8
File size: 27.3 KB
Line 
1/*
2 * TINET (TCP/IP Protocol Stack)
3 *
4 * Copyright (C) 2001-2009 by Dep. of Computer Science and Engineering
5 * Tomakomai National College of Technology, JAPAN
6 *
7 * 上記著作権者は,以下の (1)~(4) の条件か,Free Software Foundation
8 * によって公表されている GNU General Public License の Version 2 に記
9 * 述されている条件を満たす場合に限り,本ソフトウェア(本ソフトウェア
10 * を改変したものを含む.以下同じ)を使用・複製・改変・再配布(以下,
11 * 利用と呼ぶ)することを無償で許諾する.
12 * (1) 本ソフトウェアをソースコードの形で利用する場合には,上記の著作
13 * 権表示,この利用条件および下記の無保証規定が,そのままの形でソー
14 * スコード中に含まれていること.
15 * (2) 本ソフトウェアを,ライブラリ形式など,他のソフトウェア開発に使
16 * 用できる形で再配布する場合には,再配布に伴うドキュメント(利用
17 * 者マニュアルなど)に,上記の著作権表示,この利用条件および下記
18 * の無保証規定を掲載すること.
19 * (3) 本ソフトウェアを,機器に組み込むなど,他のソフトウェア開発に使
20 * 用できない形で再配布する場合には,次の条件を満たすこと.
21 * (a) 再配布に伴うドキュメント(利用者マニュアルなど)に,上記の著
22 * 作権表示,この利用条件および下記の無保証規定を掲載すること.
23 * (4) 本ソフトウェアの利用により直接的または間接的に生じるいかなる損
24 * 害からも,上記著作権者およびTOPPERSプロジェクトを免責すること.
25 *
26 * 本ソフトウェアは,無保証で提供されているものである.上記著作権者お
27 * よびTOPPERSプロジェクトは,本ソフトウェアに関して,その適用可能性も
28 * 含めて,いかなる保証も行わない.また,本ソフトウェアの利用により直
29 * 接的または間接的に生じたいかなる損害に関しても,その責任を負わない.
30 *
31 * @(#) $Id$
32 */
33
34/* $FreeBSD: src/sys/netinet6/nd6_nbr.c,v 1.13 2002/10/16 01:54:45 sam Exp $ */
35/* $KAME: nd6_nbr.c,v 1.86 2002/01/21 02:33:04 jinmei Exp $ */
36
37/*
38 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
39 * All rights reserved.
40 *
41 * Redistribution and use in source and binary forms, with or without
42 * modification, are permitted provided that the following conditions
43 * are met:
44 * 1. Redistributions of source code must retain the above copyright
45 * notice, this list of conditions and the following disclaimer.
46 * 2. Redistributions in binary form must reproduce the above copyright
47 * notice, this list of conditions and the following disclaimer in the
48 * documentation and/or other materials provided with the distribution.
49 * 3. Neither the name of the project nor the names of its contributors
50 * may be used to endorse or promote products derived from this software
51 * without specific prior written permission.
52 *
53 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
54 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
55 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
56 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
57 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
58 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
59 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
60 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
61 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
62 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
63 * SUCH DAMAGE.
64 */
65
66#include <string.h>
67
68#ifdef TARGET_KERNEL_ASP
69
70#include <kernel.h>
71#include <sil.h>
72#include <t_syslog.h>
73#include "kernel_cfg.h"
74
75#endif /* of #ifdef TARGET_KERNEL_ASP */
76
77#ifdef TARGET_KERNEL_JSP
78
79#include <s_services.h>
80#include <t_services.h>
81#include "kernel_id.h"
82
83#endif /* of #ifdef TARGET_KERNEL_JSP */
84
85#include <tinet_defs.h>
86#include <tinet_config.h>
87
88#include <net/if.h>
89#include <net/if_ppp.h>
90#include <net/if_loop.h>
91#include <net/ethernet.h>
92#include <net/if_arp.h>
93#include <net/ppp_ipcp.h>
94#include <net/net.h>
95#include <net/net_var.h>
96#include <net/net_buf.h>
97#include <net/net_timer.h>
98#include <net/net_count.h>
99
100#include <netinet/in.h>
101#include <netinet/in_var.h>
102
103#include <netinet6/in6.h>
104#include <netinet6/in6_var.h>
105#include <netinet6/nd6.h>
106
107#include <netinet/ip6.h>
108#include <netinet/icmp6.h>
109#include <netinet6/ip6_var.h>
110
111#include <net/if6_var.h>
112
113#ifdef SUPPORT_INET6
114
115/*
116 * 局所関数
117 */
118
119static void nd6_dad_timer (T_IN6_IFADDR *ifa);
120static void nd6_dad_duplicated (T_IFNET *ifp, T_IN6_IFADDR *ifa);
121static void nd6_dad_ns_output (T_IFNET *ifp, T_IN6_IFADDR *ifa);
122static void nd6_dad_ns_input (T_IFNET *ifp, T_IN6_IFADDR *ifa);
123static void nd6_dad_na_input (T_IFNET *ifp, T_IN6_IFADDR *ifa);
124
125/*
126 * nd6_dad_duplicated -- 重複アドレスを検出した時の処理
127 */
128
129static void
130nd6_dad_duplicated (T_IFNET *ifp, T_IN6_IFADDR *ifa)
131{
132 if ((ifa->flags & IN6_IFF_TENTATIVE) == 0)
133 syslog(LOG_ERROR, "[ND6 DAD DUP] no pending.");
134 else {
135 syslog(LOG_ERROR, "[ND6 DAD DUP] dup IPv6 addr: %s.", ipv62str(NULL, &ifa->addr));
136 ifa->flags &= ~IN6_IFF_TENTATIVE;
137 ifa->flags |= IN6_IFF_DUPLICATED;
138
139 /* タイマーを停止する。*/
140 untimeout((callout_func)nd6_dad_timer, ifa);
141
142 /*
143 * もし IPv6 アドレスが、ユニークに割り当てらた
144 * (イーサネット用の EUI-64)ハードウェアアドレスをベースとした
145 * インタフェース ID で生成されたのであれば、
146 * このインタフェースでの IPv6 の動作は無効にすべきである
147 * (RFC2462bis-03 セクション 5.4.5)。
148 */
149 if (IN6_IS_ADDR_LINKLOCAL(&ifa->addr))
150 ifp->flags |= ND6_IFF_IFDISABLED;
151 }
152 }
153
154/*
155 * nd6_dad_ns_output -- 重複アドレス検出 (DAD) 要請を出力する。
156 */
157
158static void
159nd6_dad_ns_output (T_IFNET *ifp, T_IN6_IFADDR *ifa)
160{
161 NET_COUNT_ICMP6(net_count_nd6[NC_ND6_DAD_OUT_PACKETS], 1);
162
163 ifa->ns_ocount ++;
164 nd6_ns_output(ifp, &in6_addr_unspecified, &ifa->addr, NULL, true);
165 }
166
167/*
168 * nd6_dad_ns_input -- 重複アドレス検出 (DAD) 要請入力
169 */
170
171static void
172nd6_dad_ns_input (T_IFNET *ifp, T_IN6_IFADDR *ifa)
173{
174 NET_COUNT_ICMP6(net_count_nd6[NC_ND6_NS_IN_PACKETS], 1);
175
176 /*
177 * まだ、重複アドレス検出要請を出力していない場合
178 */
179 if (ifa->ns_ocount == 0)
180 nd6_dad_duplicated(ifp, ifa);
181 else
182 ifa->ns_icount ++;
183 }
184
185/*
186 * nd6_dad_na_input -- 重複アドレス検出 (DAD) 通知入力
187 */
188
189static void
190nd6_dad_na_input (T_IFNET *ifp, T_IN6_IFADDR *ifa)
191{
192 NET_COUNT_ICMP6(net_count_nd6[NC_ND6_NA_IN_PACKETS], 1);
193 ifa->na_icount ++;
194 nd6_dad_duplicated(ifp, ifa);
195 }
196
197/*
198 * nd6_dad_timer -- 重複アドレス検出 (DAD) タイマー
199 */
200
201static void
202nd6_dad_timer (T_IN6_IFADDR *ifa)
203{
204 T_IFNET *ifp = IF_GET_IFNET();
205
206 if (ifa->flags & IN6_IFF_DUPLICATED) {
207 syslog(LOG_ERROR, "[ND6 DAD] dup IPv6 addr: %s.", ipv62str(NULL, &ifa->addr));
208 return;
209 }
210
211 if ((ifa->flags & IN6_IFF_TENTATIVE) == 0) {
212 syslog(LOG_ERROR, "[ND6 DAD] no tentative IPv6 addr: %s.", ipv62str(NULL, &ifa->addr));
213 return;
214 }
215
216 if (ifa->ns_ocount < NUM_IP6_DAD_COUNT) {
217 nd6_dad_ns_output(ifp, ifa);
218
219 /* タイムアウトを近隣探索の送信間隔に設定する。*/
220 timeout((callout_func)nd6_dad_timer, ifa, ND6_RETRANS_TIME * NET_TIMER_HZ / SYSTIM_HZ);
221 }
222 else if (ifa->na_icount || ifa->ns_icount)
223 nd6_dad_duplicated(ifp, ifa);
224 else {
225 ifa->flags &= ~IN6_IFF_TENTATIVE;
226 syslog(LOG_NOTICE, "[ND6 DAD] no dup IPv6 addr: %s.", ipv62str(NULL, &ifa->addr));
227 }
228 }
229
230/*
231 * nd6_ns_input -- 近隣要請の入力処理。
232 */
233
234void
235nd6_ns_input (T_NET_BUF *input, uint_t off)
236{
237 T_IFNET *ifp = IF_GET_IFNET();
238 T_IP6_HDR *ip6h;
239 T_NEIGHBOR_SOLICIT_HDR *nsh;
240 T_ND_OPT_HDR *opth;
241 T_IN6_IFADDR *ifa;
242 bool_t tlladdr;
243 uint_t lladdr_len = 0;
244 uint32_t flags;
245 uint8_t *lladdr = NULL;
246 uint8_t nd_opt_off[ND_OPT_OFF_ARRAY_SIZE];
247
248 NET_COUNT_ICMP6(net_count_nd6[NC_ND6_NS_IN_PACKETS], 1);
249
250 ip6h = GET_IP6_HDR(input);
251
252 /*
253 * ヘッダのチェック、以下の場合は破棄する。
254 * ・ホップリミットが IPV6_MAXHLIM (255) 以外
255 * ・ヘッダ長が短い
256 */
257 if (ip6h->hlim != IPV6_MAXHLIM || input->len - off < NEIGHBOR_SOLICIT_HDR_SIZE)
258 goto err_ret;
259
260 nsh = (T_NEIGHBOR_SOLICIT_HDR *)(input->buf + off);
261
262 if (IN6_IS_ADDR_UNSPECIFIED(&ip6h->src)) {
263 /*
264 * 始点アドレスが無指定なら、重複アドレス検出
265 * あて先アドレスは、要請マルチキャストでなければならない。
266 */
267 if (!IN6_IS_ADDR_NS_MULTICAST(&ip6h->dst))
268 goto err_ret;
269 }
270
271 /* 目的アドレスがマルチキャストならエラー */
272 if (IN6_IS_ADDR_MULTICAST(&nsh->target))
273 goto err_ret;
274
275 /* 近隣探索オプションのオフセットを記録する。*/
276 if (nd6_options(nd_opt_off, input->buf + (off + NEIGHBOR_SOLICIT_HDR_SIZE),
277 input->len - (off + NEIGHBOR_SOLICIT_HDR_SIZE)) != E_OK)
278 goto err_ret;
279
280 /* 近隣探索オプション (始点リンクアドレス) */
281 if (nd_opt_off[ND_OPT_OFF_ARRAY_IX(ND_OPT_SOURCE_LINKADDR)]) {
282 opth = (T_ND_OPT_HDR *)((uint8_t *)(input->buf + off + NEIGHBOR_SOLICIT_HDR_SIZE) +
283 nd_opt_off[ND_OPT_OFF_ARRAY_IX(ND_OPT_SOURCE_LINKADDR)] - 8);
284 /* 注意: オプションオフセット配列には、オフセット + 8 が設定されている。*/
285 lladdr = (uint8_t *)(opth + 1);
286 lladdr_len = (opth->len << 3);
287 }
288
289 if (IN6_IS_ADDR_UNSPECIFIED(&ip6h->src) && lladdr != NULL)
290 goto err_ret;
291
292 /* 宛先アドレスがマルチキャストなら近隣通知にデータリンク層のアドレスを付ける。*/
293 if (IN6_IS_ADDR_MULTICAST(&ip6h->dst))
294 tlladdr = true;
295 else
296 tlladdr = false;
297
298 /*
299 * 目的アドレスが、自分のネットワークインタフェースに
300 * 割り当てられているアドレスか調べる。
301 * なお、代理サービスは実装していない。
302 */
303 ifa = in6_lookup_ifaddr(ifp, &nsh->target);
304
305 if (ifa == NULL)
306 goto free_ret;
307
308 /* 探索結果アドレスが重複していれば応答しないで終了する。*/
309 if (ifa->flags & IN6_IFF_DUPLICATED)
310 goto err_ret;
311
312 /*
313 * ネットワークインタフェースのアドレス長が一致しなければエラー
314 */
315 if (lladdr && lladdr_len != ((sizeof(T_IF_ADDR) + sizeof(T_ND_OPT_HDR) + 7) & ~7))
316 goto err_ret;
317
318 /*
319 * 始点アドレスが自分のアドレスと一致すれば重複している。
320 */
321 if (IN6_ARE_ADDR_EQUAL(&ifa->addr, &ip6h->src))
322 goto free_ret;
323
324 /* 重複検出中に、近隣要請を受信したときの処理 */
325 if (ifa->flags & IN6_IFF_TENTATIVE) {
326 if (IN6_IS_ADDR_UNSPECIFIED(&ip6h->src))
327 nd6_dad_ns_input(ifp, ifa);
328 goto free_ret;
329 }
330
331 if ((ifa->flags & IN6_IFF_ANYCAST) || !tlladdr)
332 flags = 0;
333 else
334 flags = ND_NA_FLG_OVERRIDE;
335
336 /*
337 * 始点アドレスが無指定なら、送信相手は重複アドレス検出中で、
338 * 直接送信することはできないので、全ノードマルチキャスト
339 * アドレスに送信する。
340 */
341 if (IN6_IS_ADDR_UNSPECIFIED(&ip6h->src)) {
342 nd6_na_output(ifp, &in6_addr_linklocal_allnodes, &nsh->target, flags, tlladdr);
343 goto free_ret;
344 }
345
346 /* 近隣キャッシュに登録する。*/
347 nd6_cache_lladdr(ifp, &ip6h->src, (T_IF_ADDR *)lladdr, ND_NEIGHBOR_SOLICIT, 0);
348
349 nd6_na_output(ifp, &ip6h->src, &nsh->target, flags | ND_NA_FLG_SOLICITED, tlladdr);
350
351free_ret:
352 syscall(rel_net_buf(input));
353 return;
354
355err_ret:
356 NET_COUNT_ICMP6(net_count_nd6[NC_ICMP6_IN_ERR_PACKETS], 1);
357 syscall(rel_net_buf(input));
358 }
359
360/*
361 * nd6_ns_output -- 近隣要請を出力する。
362 */
363
364void
365nd6_ns_output (T_IFNET *ifp, T_IN6_ADDR *daddr,
366 T_IN6_ADDR *taddr, T_LLINFO_ND6 *ln, bool_t dad)
367{
368 T_NEIGHBOR_SOLICIT_HDR *nsh;
369 T_NET_BUF *output;
370 T_IP6_HDR *ip6h;
371 T_IF_ADDR *mac = NULL;
372 uint_t len;
373 uint16_t ipflags = 0;
374
375 NET_COUNT_ICMP6(net_count_nd6[NC_ND6_NS_OUT_PACKETS], 1);
376
377 if (IN6_IS_ADDR_MULTICAST(taddr))
378 return;
379
380 /* 近隣要請ペイロード長を計算する。*/
381 if (!dad && IF_SOFTC_TO_IFADDR(ifp->ic))
382 len = (NEIGHBOR_SOLICIT_HDR_SIZE + ND_OPT_HDR_SIZE + sizeof(T_IF_ADDR) + 7) >> 3 << 3;
383 else
384 len = (NEIGHBOR_SOLICIT_HDR_SIZE + 7) >> 3 << 3;
385
386 /*
387 * 要請マルチキャスト・アドレス宛の近隣探索では、
388 * 他のリンクからの偽造されたデータグラムを
389 * 排除するため、ホップリミットに IPV6_MAXHLIM (255) を設定する。
390 */
391 if (IN6_IS_ADDR_UNSPECIFIED(daddr) || IN6_IS_ADDR_MULTICAST(daddr))
392 ipflags = IPV6_OUT_SET_HOP_LIMIT(IPV6_OUT_FLG_HOP_LIMIT, IPV6_MAXHLIM);
393
394 /* ネットワークバッファを獲得し、IPv6 ヘッダを設定する。*/
395 if (in6_get_datagram(&output, len, 0, daddr, NULL,
396 IPPROTO_ICMPV6, IPV6_MAXHLIM,
397 NBA_SEARCH_ASCENT, TMO_ND6_NS_OUTPUT) != E_OK)
398 return;
399
400 ip6h = GET_IP6_HDR(output);
401
402 if (IN6_IS_ADDR_UNSPECIFIED(daddr)) {
403
404 /* 宛先アドレスが無指定の場合は、要請マルチキャストアドレスを設定する。*/
405 ip6h->dst.s6_addr32[0] = IPV6_ADDR_INT32_MLL;
406 ip6h->dst.s6_addr32[2] = IPV6_ADDR_INT32_ONE;
407 ip6h->dst.s6_addr32[3] = taddr->s6_addr32[3];
408 ip6h->dst.s6_addr8[12] = 0xff;
409 }
410
411 /* 送信元アドレスの設定 */
412 if (!dad) {
413
414 /* 重複アドレス検出ではない時の送信元アドレス決定処理。*/
415 T_IN6_ADDR *saddr;
416
417 if (ln && ln->hold) {
418 /*
419 * 送信がペンディングされているデータグラムの
420 * 送信元アドレスを利用する。
421 */
422 if (ln->hold->len > IF_IP6_HDR_SIZE)
423 saddr = &GET_IP6_HDR(ln->hold)->src;
424 else
425 saddr = NULL;
426 }
427 else
428 saddr = NULL;
429
430 if (saddr && in6_lookup_ifaddr(ifp, saddr))
431 ip6h->src = *saddr;
432 else {
433 /*
434 * 宛先アドレスにふさわしい送信元アドレスを、
435 * ネットワークインタフェースから探索して利用する。
436 */
437 T_IN6_IFADDR *ifa;
438
439 if ((ifa = in6_ifawithifp(ifp, &ip6h->dst)) == NULL) {
440 syscall(rel_net_buf(output));
441 return;
442 }
443 ip6h->src = ifa->addr;
444 }
445 }
446 else {
447
448 /* 重複アドレス検出時の送信元アドレスは無指定。*/
449 memset(&ip6h->src, 0, sizeof(T_IN6_ADDR));
450 }
451
452 /* 近隣要請ヘッダを設定する。*/
453 nsh = GET_NEIGHBOR_SOLICIT_HDR(output, IF_IP6_NEIGHBOR_SOLICIT_HDR_OFFSET);
454 nsh->hdr.type = ND_NEIGHBOR_SOLICIT;
455 nsh->hdr.code = 0;
456 nsh->hdr.data.data32 = 0;
457 nsh->target = *taddr;
458
459 if (!dad && (mac = IF_SOFTC_TO_IFADDR(ifp->ic)) != NULL) {
460
461 /* 近隣探索オプションとして、探索目標の MAC アドレスを設定する。*/
462 T_ND_OPT_HDR *opth;
463 uint_t optlen;
464
465 opth = (T_ND_OPT_HDR *)GET_NEIGHBOR_SOLICIT_SDU(output, IF_IP6_NEIGHBOR_SOLICIT_HDR_OFFSET);
466 optlen = (ND_OPT_HDR_SIZE + sizeof(T_IF_ADDR) + 7) >> 3 << 3;
467 memset(opth, 0, optlen);
468 opth->type = ND_OPT_SOURCE_LINKADDR;
469 opth->len = optlen >> 3;
470 memcpy((uint8_t *)opth + sizeof(T_ND_OPT_HDR), mac, sizeof(T_IF_ADDR));
471 }
472
473 /* チェックサムを計算する。*/
474 nsh->hdr.sum = 0;
475 nsh->hdr.sum = in6_cksum(output, IPPROTO_ICMPV6, (uint8_t*)nsh - output->buf, len);
476
477 /* 送信する。*/
478 NET_COUNT_ICMP6(net_count_nd6[NC_ICMP6_OUT_OCTETS],
479 output->len - GET_IF_IP6_HDR_SIZE(output));
480 NET_COUNT_ICMP6(net_count_nd6[NC_ICMP6_OUT_PACKETS], 1);
481 NET_COUNT_MIB(icmp6_ifstat.ipv6IfIcmpOutMsgs, 1);
482 NET_COUNT_MIB(icmp6_ifstat.ipv6IfIcmpOutNeighborSolicits, 1);
483 ip6_output(output, ipflags | IPV6_OUT_FLG_DAD, TMO_ND6_NS_OUTPUT);
484 }
485
486/*
487 * nd6_na_input -- 近隣通知の入力処理。
488 */
489
490void
491nd6_na_input (T_NET_BUF *input, uint_t off)
492{
493 T_IFNET *ifp = IF_GET_IFNET();
494 T_IP6_HDR *ip6h;
495 T_NEIGHBOR_ADVERT_HDR *nah;
496 T_ND_OPT_HDR *opth;
497 T_IN6_IFADDR *ifa;
498 T_LLINFO_ND6 *ln;
499 SYSTIM now;
500 bool_t llchange;
501 uint_t lladdr_len = 0;
502 uint8_t *lladdr = NULL;
503 uint8_t nd_opt_off[ND_OPT_OFF_ARRAY_SIZE];
504
505 NET_COUNT_ICMP6(net_count_nd6[NC_ND6_NS_IN_PACKETS], 1);
506
507 ip6h = GET_IP6_HDR(input);
508
509 /*
510 * ヘッダのチェック、以下の場合は破棄する。
511 * ・ホップリミットが IPV6_MAXHLIM (255) 以外
512 * ・ヘッダ長が短い
513 */
514 if (ip6h->hlim != IPV6_MAXHLIM || input->len - off < NEIGHBOR_ADVERT_HDR_SIZE)
515 goto err_ret;
516
517 nah = (T_NEIGHBOR_ADVERT_HDR *)(input->buf + off);
518
519 /* 目的アドレスがマルチキャストならエラー */
520 if (IN6_IS_ADDR_MULTICAST(&nah->target))
521 goto err_ret;
522
523 /* 近隣要請への応答で、宛先アドレスがマルチキャストならエラー */
524 if ((nah->nd_na_flags_reserved & ND_NA_FLG_SOLICITED) &&
525 IN6_IS_ADDR_MULTICAST(&ip6h->dst))
526 goto err_ret;
527
528 /* 近隣探索オプションのオフセットを記録する。*/
529 if (nd6_options(nd_opt_off, input->buf + (off + NEIGHBOR_ADVERT_HDR_SIZE),
530 input->len - (off + NEIGHBOR_ADVERT_HDR_SIZE)) != E_OK)
531 goto err_ret;
532
533 /* 近隣探索オプション (目的リンクアドレス) */
534 if (nd_opt_off[ND_OPT_OFF_ARRAY_IX(ND_OPT_TARGET_LINKADDR)]) {
535 opth = (T_ND_OPT_HDR *)((uint8_t *)(input->buf + off + NEIGHBOR_ADVERT_HDR_SIZE) +
536 nd_opt_off[ND_OPT_OFF_ARRAY_IX(ND_OPT_TARGET_LINKADDR)] - 8);
537 /* 注意: オプションオフセット配列には、オフセット + 8 が設定されている。*/
538 lladdr = (uint8_t *)(opth + 1);
539 lladdr_len = (opth->len << 3);
540 }
541
542 ifa = in6_lookup_ifaddr(ifp, &nah->target);
543
544 /*
545 * 目的アドレスが自分のネットワークインタフェースに割り当てられているアドレスの
546 * いずれかに一致したときは、重複していることを意味している。
547 */
548 if (ifa) {
549 if (ifa->flags & IN6_IFF_TENTATIVE)
550 nd6_dad_na_input(ifp, ifa);
551 else
552 syslog(LOG_ERROR, "[ND6 NA INPUT] dup IPv6 addr: %s.", ipv62str(NULL, &ifa->addr));
553 goto err_ret;
554 }
555
556 /*
557 * ネットワークインタフェースのアドレス長が一致しなければエラー
558 */
559 if (lladdr && lladdr_len != ((sizeof(T_IF_ADDR) + sizeof(T_ND_OPT_HDR) + 7) & ~7))
560 goto err_ret;
561
562 /* 近隣キャッシュを探索する。*/
563 syscall(wai_sem(SEM_ND6_CACHE));
564 if ((ln = nd6_lookup(&nah->target, false)) == NULL)
565 goto free_ret;
566
567 /* 近隣キャッシュの状態が、データリンク層のアドレス未解決の場合 */
568 if (ln->state == ND6_LLINFO_INCOMPLETE) {
569 /* 通知されたデータリンク層のアドレスの長さが 0 の場合 */
570 if (lladdr == NULL)
571 goto free_ret;
572
573 ln->ifaddr = *(T_IF_ADDR *)lladdr;
574 if (nah->nd_na_flags_reserved & ND_NA_FLG_SOLICITED) {
575 ln->state = ND6_LLINFO_REACHABLE;
576 /*ln->byhint = 0*/;
577 if (ln->expire) {
578 syscall(get_tim(&now));
579 ln->expire = now + ND6_REACHABLE_TIME;
580 }
581 }
582 else {
583 syscall(get_tim(&now));
584 ln->expire = now + ND6_GCOLLECTION_TIME;
585 ln->state = ND6_LLINFO_STALE;
586 }
587
588 if ((nah->nd_na_flags_reserved & ND_NA_FLG_ROUTER) != 0)
589 ln->flags |= ND6_LLIF_ROUTER;
590 else
591 ln->flags &= ~ND6_LLIF_ROUTER;
592 if ((ln->flags & ND6_LLIF_ROUTER) != 0)
593 /*pfxlist_onlink_check()*/;
594 }
595 else {
596 if (lladdr == NULL)
597 llchange = false;
598 else if (memcmp(lladdr, &ln->ifaddr, sizeof(T_IF_ADDR)))
599 llchange = true;
600 else
601 llchange = false;
602
603 /*
604 * 状態遷移表
605 *
606 * nd_na_flags_reserved
607 * OVERRIDE SOLICTED lladdr llchange 処理 (L: lladdr を登録する)
608 *
609 * F F N - (2c)
610 * F F Y F (2b) L
611 * F F Y T (1) REACHABLE -> STALE
612 * F T N - (2c) * -> REACHABLE
613 * F T Y F (2b) L * -> REACHABLE
614 * F T Y T (1) REACHABLE -> STALE
615 * T F N - (2a)
616 * T F Y F (2a) L
617 * T F Y T (2a) L *-> STALE
618 * T T N - (2a) * -> REACHABLE
619 * T T Y F (2a) L * -> REACHABLE
620 * T T Y T (2a) L * -> REACHABLE
621 */
622 if ((nah->nd_na_flags_reserved & ND_NA_FLG_OVERRIDE) == 0 && (lladdr != NULL && llchange)) { /* (1) */
623 /* 状態が REACHABLE なら STALE に遷移する。*/
624 if (ln->state == ND6_LLINFO_REACHABLE) {
625 syscall(get_tim(&now));
626 ln->expire = now + ND6_GCOLLECTION_TIME;
627 ln->state = ND6_LLINFO_STALE;
628 }
629 goto free_ret;
630 }
631 else if ((nah->nd_na_flags_reserved & ND_NA_FLG_OVERRIDE) || /* (2a) */
632 ((nah->nd_na_flags_reserved & ND_NA_FLG_OVERRIDE) == 0 && (lladdr != NULL && !llchange)) || /* (2b) */
633 lladdr == NULL) { /* (2c) */
634
635 /* データリンク層のアドレスが通知されていれば更新する。*/
636 if (lladdr != NULL)
637 ln->ifaddr = *(T_IF_ADDR *)lladdr;
638
639 /* 近隣要請への応答なら REACHABLE に遷移する。*/
640 if (nah->nd_na_flags_reserved & ND_NA_FLG_SOLICITED) {
641 ln->state = ND6_LLINFO_REACHABLE;
642 /*ln->byhint = 0*/;
643 if (ln->expire) {
644 syscall(get_tim(&now));
645 ln->expire = now + ND6_REACHABLE_TIME;
646 }
647 }
648 /*
649 * データリンク層のアドレスが通知され、
650 * 異なるアドレスなら更新する。
651 */
652 else if (lladdr != NULL && llchange) {
653 syscall(get_tim(&now));
654 ln->expire = now + ND6_GCOLLECTION_TIME;
655 ln->state = ND6_LLINFO_STALE;
656 }
657 }
658
659 /* ルータ通知フラグの処理 */
660 if ((ln->flags & ND6_LLIF_ROUTER ) != 0 &&
661 (nah->nd_na_flags_reserved & ND_NA_FLG_ROUTER) == 0) {
662 /*
663 * 送信相手がルータ通知フラグを無効にした場合。
664 * ディフォルト・ルータリストから対象のルータを削除し、
665 * 近隣探索キャッシュを更新する。
666 */
667 T_DEF_ROUTER *dr = NULL;
668
669 syscall(wai_sem(SEM_ND6_DEFRTRLIST));
670 if ((dr = nd6_defrtrlist_lookup(&ln->addr)) != NULL) {
671 nd6_defrtrlist_del(dr);
672 }
673 syscall(sig_sem(SEM_ND6_DEFRTRLIST));
674 }
675 if ((nah->nd_na_flags_reserved & ND_NA_FLG_ROUTER) != 0)
676 ln->flags |= ND6_LLIF_ROUTER;
677 else
678 ln->flags &= ~ND6_LLIF_ROUTER;
679 }
680
681 ln->asked = 0;
682
683 /* アドレス解決待ちのデータグラムがあれば送信する。*/
684 nd6_output_hold(ifp, ln);
685
686free_ret:
687 syscall(sig_sem(SEM_ND6_CACHE));
688 syscall(rel_net_buf(input));
689 return;
690
691err_ret:
692 NET_COUNT_ICMP6(net_count_nd6[NC_ICMP6_IN_ERR_PACKETS], 1);
693 syscall(rel_net_buf(input));
694 }
695
696/*
697 * nd6_na_output -- 近隣通知を出力する。
698 */
699
700void
701nd6_na_output (T_IFNET *ifp, T_IN6_ADDR *daddr,
702 T_IN6_ADDR *taddr, uint32_t flags, bool_t tlladdr)
703{
704 T_NEIGHBOR_ADVERT_HDR *nah;
705 T_NET_BUF *output;
706 T_IP6_HDR *ip6h;
707 T_IN6_IFADDR *ifa;
708 T_IF_ADDR *mac = NULL;
709 uint_t len;
710 uint16_t ipflags = 0;
711
712 NET_COUNT_ICMP6(net_count_nd6[NC_ND6_NA_OUT_PACKETS], 1);
713
714 /*
715 * 近隣通知ペイロード長を計算する。
716 * tlladdr が真ならネットワークインタフェースのアドレスを追加する。
717 */
718 if (tlladdr && IF_SOFTC_TO_IFADDR(ifp->ic))
719 len = (NEIGHBOR_ADVERT_HDR_SIZE + ND_OPT_HDR_SIZE + sizeof(T_IF_ADDR) + 7) >> 3 << 3;
720 else
721 len = (NEIGHBOR_ADVERT_HDR_SIZE + 7) >> 3 << 3;
722
723 /*
724 * 他のリンクからの偽造されたデータグラムを
725 * 排除するため、ホップリミットに IPV6_MAXHLIM (255) を設定する。
726 */
727 ipflags = IPV6_OUT_SET_HOP_LIMIT(IPV6_OUT_FLG_HOP_LIMIT, IPV6_MAXHLIM);
728
729 /* ネットワークバッファを獲得し、IPv6 ヘッダを設定する。*/
730 if (in6_get_datagram(&output, len, 0, daddr, NULL,
731 IPPROTO_ICMPV6, IPV6_MAXHLIM,
732 NBA_SEARCH_ASCENT, TMO_ND6_NA_OUTPUT) != E_OK)
733 return;
734
735 ip6h = GET_IP6_HDR(output);
736
737 if (IN6_IS_ADDR_UNSPECIFIED(daddr)) {
738 /*
739 * 宛先アドレスが無指定の場合は、
740 * リンクローカル全ノード・マルチキャストアドレスを設定する。
741 */
742 ip6h->dst.s6_addr32[0] = IPV6_ADDR_INT32_MLL;
743 ip6h->dst.s6_addr32[1] = 0;
744 ip6h->dst.s6_addr32[2] = 0;
745 ip6h->dst.s6_addr32[3] = IPV6_ADDR_INT32_ONE;
746 flags &= ~ND_NA_FLG_SOLICITED;
747 }
748 else
749 ip6h->dst = *daddr;
750
751 /*
752 * 宛先アドレスにふさわしい送信元アドレスを、
753 * ネットワークインタフェースから探索して利用する。
754 */
755 if ((ifa = in6_ifawithifp(ifp, &ip6h->dst)) == NULL) {
756 syscall(rel_net_buf(output));
757 return;
758 }
759 ip6h->src = ifa->addr;
760
761 /* 近隣通知ヘッダを設定する。*/
762 nah = GET_NEIGHBOR_ADVERT_HDR(output, IF_IP6_NEIGHBOR_ADVERT_HDR_OFFSET);
763 nah->hdr.type = ND_NEIGHBOR_ADVERT;
764 nah->hdr.code = 0;
765 nah->target = *taddr;
766
767 /* tlladdr が真ならネットワークインタフェースのアドレスを追加する。*/
768 if (tlladdr && (mac = IF_SOFTC_TO_IFADDR(ifp->ic)) != NULL) {
769
770 T_ND_OPT_HDR *opth;
771 uint_t optlen;
772
773 opth = (T_ND_OPT_HDR *)GET_NEIGHBOR_ADVERT_SDU(output, IF_IP6_NEIGHBOR_ADVERT_HDR_OFFSET);
774 optlen = (ND_OPT_HDR_SIZE + sizeof(T_IF_ADDR) + 7) >> 3 << 3;
775 memset(opth, 0, optlen);
776 opth->type = ND_OPT_TARGET_LINKADDR;
777 opth->len = optlen >> 3;
778 memcpy((uint8_t *)opth + sizeof(T_ND_OPT_HDR), mac, sizeof(T_IF_ADDR));
779 }
780 else
781 flags &= ~ND_NA_FLG_OVERRIDE;
782
783 nah->nd_na_flags_reserved = flags;
784
785 /* チェックサムを計算する。*/
786 nah->hdr.sum = 0;
787 nah->hdr.sum = in6_cksum(output, IPPROTO_ICMPV6, (uint8_t*)nah - output->buf, len);
788
789 /* 送信する。*/
790 NET_COUNT_ICMP6(net_count_nd6[NC_ICMP6_OUT_OCTETS],
791 output->len - GET_IF_IP6_HDR_SIZE(output));
792 NET_COUNT_ICMP6(net_count_nd6[NC_ICMP6_OUT_PACKETS], 1);
793 NET_COUNT_MIB(icmp6_ifstat.ipv6IfIcmpOutMsgs, 1);
794 NET_COUNT_MIB(icmp6_ifstat.ipv6IfIcmpOutNeighborAdvertisements, 1);
795 ip6_output(output, ipflags, TMO_ND6_NS_OUTPUT);
796 }
797
798/*
799 * nd6_dad_start -- 重複アドレス検出 (DAD) を開始する。
800 *
801 * 第3引数のtick は、インタフェースが起動されてから、重複検出を
802 * 送信するまでの最小遅延値である。
803 */
804
805void
806nd6_dad_start (T_IFNET *ifp, T_IN6_IFADDR *ifa, int_t *tick)
807{
808 /* フラグをチェックする。*/
809 if (!(ifa->flags & IN6_IFF_TENTATIVE))
810 return;
811
812#if NUM_IP6_DAD_COUNT == 0
813
814 ifa->flags &= ~IN6_IFF_TENTATIVE;
815
816#else /* of #if NUM_IP6_DAD_COUNT == 0 */
817
818 if (ifa->flags & IN6_IFF_ANYCAST) {
819 ifa->flags &= ~IN6_IFF_TENTATIVE;
820 return;
821 }
822 else if ((ifa->flags & IN6_IFF_TENTATIVE) == 0)
823 return;
824
825 /* 重複アドレス検出カウンタをリセットする。*/
826 ifa->ns_icount = ifa->na_icount = ifa->ns_ocount = 0;
827
828 /*
829 * 重複アドレス検出要請を送信する。ただし、インタフェースを
830 * 初期化した直後に、最初に送信する場合は、ランダムな遅延を行う。
831 */
832 if (tick == NULL) {
833 nd6_dad_ns_output(ifp, ifa);
834
835 /* タイムアウトを近隣探索の送信間隔に設定する。*/
836 timeout((callout_func)nd6_dad_timer, ifa, ND6_RETRANS_TIME * NET_TIMER_HZ / SYSTIM_HZ);
837 }
838 else {
839 int_t ntick;
840
841 if (*tick == 0) /* 最初の送信 */
842 ntick = net_rand() % (ND6_FIRST_DAD_DELAY_TIME * NET_TIMER_HZ / SYSTIM_HZ);
843 else
844 ntick = *tick + net_rand() % (ND6_DAD_DELAY_TIME * NET_TIMER_HZ / SYSTIM_HZ);
845 *tick = ntick;
846 timeout((callout_func)nd6_dad_timer, ifa, ntick);
847 }
848
849#endif /* of #if NUM_IP6_DAD_COUNT == 0 */
850
851 }
852
853#endif /* of #ifdef SUPPORT_INET6 */
Note: See TracBrowser for help on using the repository browser.