source: azure_iot_hub/trunk/asp3_dcre/tinet/netinet6/nd6_nbr.c@ 389

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

ビルドが通るよう更新

  • Property svn:eol-style set to native
  • Property svn:mime-type set to text/x-csrc;charset=UTF-8
File size: 27.4 KB
Line 
1/*
2 * TINET (TCP/IP Protocol Stack)
3 *
4 * Copyright (C) 2001-2017 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_endian.h>
96#include <net/net_var.h>
97#include <net/net_buf.h>
98#include <net/net_timer.h>
99#include <net/net_count.h>
100
101#include <netinet/in.h>
102#include <netinet/in_var.h>
103#include <netinet/ip.h>
104#include <netinet/ip_var.h>
105#include <netinet/ip_icmp.h>
106
107#include <netinet6/nd6.h>
108
109#include <net/if6_var.h>
110
111#ifdef _IP6_CFG
112
113/*
114 * 局所関数
115 */
116
117static void nd6_dad_timer (T_IN6_IFADDR *ifa);
118static void nd6_dad_duplicated (T_IFNET *ifp, T_IN6_IFADDR *ifa);
119static void nd6_dad_ns_output (T_IFNET *ifp, T_IN6_IFADDR *ifa);
120static void nd6_dad_ns_input (T_IFNET *ifp, T_IN6_IFADDR *ifa);
121static void nd6_dad_na_input (T_IFNET *ifp, T_IN6_IFADDR *ifa);
122
123/*
124 * nd6_dad_duplicated -- 重複アドレスを検出した時の処理
125 */
126
127static void
128nd6_dad_duplicated (T_IFNET *ifp, T_IN6_IFADDR *ifa)
129{
130 if ((ifa->flags & IN6_IFF_TENTATIVE) == 0)
131 syslog(LOG_ERROR, "[ND6 DAD DUP] no pending.");
132 else {
133 syslog(LOG_ERROR, "[ND6 DAD DUP] dup IPv6 addr: %s.", ipv62str(NULL, &ifa->addr));
134 ifa->flags &= ~IN6_IFF_TENTATIVE;
135 ifa->flags |= IN6_IFF_DUPLICATED;
136
137 /* タイマーを停止する。*/
138 untimeout((callout_func)nd6_dad_timer, ifa);
139
140 /*
141 * もし IPv6 アドレスが、ユニークに割り当てらた
142 * (イーサネット用の EUI-64)ハードウェアアドレスをベースとした
143 * インタフェース ID で生成されたのであれば、
144 * このインタフェースでの IPv6 の動作は無効にすべきである
145 * (RFC2462bis-03 セクション 5.4.5)。
146 */
147 if (IN6_IS_ADDR_LINKLOCAL(&ifa->addr))
148 ifp->flags |= ND6_IFF_IFDISABLED;
149 }
150 }
151
152/*
153 * nd6_dad_ns_output -- 重複アドレス検出 (DAD) 要請を出力する。
154 */
155
156static void
157nd6_dad_ns_output (T_IFNET *ifp, T_IN6_IFADDR *ifa)
158{
159 NET_COUNT_ICMP6(net_count_nd6[NC_ND6_DAD_OUT_PACKETS], 1);
160
161 ifa->ns_ocount ++;
162 nd6_ns_output(ifp, &in6_addr_unspecified, &ifa->addr, NULL, true);
163 }
164
165/*
166 * nd6_dad_ns_input -- 重複アドレス検出 (DAD) 要請入力
167 */
168
169static void
170nd6_dad_ns_input (T_IFNET *ifp, T_IN6_IFADDR *ifa)
171{
172 NET_COUNT_ICMP6(net_count_nd6[NC_ND6_NS_IN_PACKETS], 1);
173
174 /*
175 * まだ、重複アドレス検出要請を出力していない場合
176 */
177 if (ifa->ns_ocount == 0)
178 nd6_dad_duplicated(ifp, ifa);
179 else
180 ifa->ns_icount ++;
181 }
182
183/*
184 * nd6_dad_na_input -- 重複アドレス検出 (DAD) 通知入力
185 */
186
187static void
188nd6_dad_na_input (T_IFNET *ifp, T_IN6_IFADDR *ifa)
189{
190 NET_COUNT_ICMP6(net_count_nd6[NC_ND6_NA_IN_PACKETS], 1);
191 ifa->na_icount ++;
192 nd6_dad_duplicated(ifp, ifa);
193 }
194
195/*
196 * nd6_dad_timer -- 重複アドレス検出 (DAD) タイマー
197 */
198
199static void
200nd6_dad_timer (T_IN6_IFADDR *ifa)
201{
202 T_IFNET *ifp = IF_GET_IFNET();
203
204 if (ifa->flags & IN6_IFF_DUPLICATED) {
205 syslog(LOG_ERROR, "[ND6 DAD] dup IPv6 addr: %s.", ipv62str(NULL, &ifa->addr));
206 return;
207 }
208
209 if ((ifa->flags & IN6_IFF_TENTATIVE) == 0) {
210 syslog(LOG_ERROR, "[ND6 DAD] no tentative IPv6 addr: %s.", ipv62str(NULL, &ifa->addr));
211 return;
212 }
213
214 if (ifa->ns_ocount < NUM_IP6_DAD_COUNT) {
215 nd6_dad_ns_output(ifp, ifa);
216
217 /* タイムアウトを近隣探索の送信間隔に設定する。*/
218 timeout((callout_func)nd6_dad_timer, ifa, ND6_RETRANS_TIME * NET_TIMER_HZ / SYSTIM_HZ);
219 }
220 else if (ifa->na_icount || ifa->ns_icount)
221 nd6_dad_duplicated(ifp, ifa);
222 else {
223 ifa->flags &= ~IN6_IFF_TENTATIVE;
224 syslog(LOG_NOTICE, "[ND6 DAD] no dup IPv6 addr: %s.", ipv62str(NULL, &ifa->addr));
225 }
226 }
227
228/*
229 * nd6_ns_input -- 近隣要請の入力処理。
230 */
231
232void
233nd6_ns_input (T_NET_BUF *input, uint_t off)
234{
235 T_IFNET *ifp = IF_GET_IFNET();
236 T_IP6_HDR *ip6h;
237 T_NEIGHBOR_SOLICIT_HDR *nsh;
238 T_ND_OPT_HDR *opth;
239 T_IN6_IFADDR *ifa;
240 bool_t tlladdr;
241 uint_t lladdr_len = 0;
242 uint32_t flags;
243 uint8_t *lladdr = NULL;
244 uint8_t nd_opt_off[ND_OPT_OFF_ARRAY_SIZE];
245
246 NET_COUNT_ICMP6(net_count_nd6[NC_ND6_NS_IN_PACKETS], 1);
247
248 ip6h = GET_IP6_HDR(input);
249
250 /*
251 * ヘッダのチェック、以下の場合は破棄する。
252 * ・ホップリミットが IPV6_MAXHLIM (255) 以外
253 * ・ヘッダ長が短い
254 */
255 if (ip6h->hlim != IPV6_MAXHLIM || input->len - off < NEIGHBOR_SOLICIT_HDR_SIZE)
256 goto err_ret;
257
258 nsh = (T_NEIGHBOR_SOLICIT_HDR *)(input->buf + off);
259
260 if (IN6_IS_ADDR_UNSPECIFIED(&ip6h->src)) {
261 /*
262 * 始点アドレスが無指定なら、重複アドレス検出
263 * あて先アドレスは、要請マルチキャストでなければならない。
264 */
265 if (!IN6_IS_ADDR_NS_MULTICAST(&ip6h->dst))
266 goto err_ret;
267 }
268
269 /* 目的アドレスがマルチキャストならエラー */
270 if (IN6_IS_ADDR_MULTICAST(&nsh->target))
271 goto err_ret;
272
273 /* 近隣探索オプションのオフセットを記録する。*/
274 if (nd6_options(nd_opt_off, input->buf + (off + NEIGHBOR_SOLICIT_HDR_SIZE),
275 input->len - (off + NEIGHBOR_SOLICIT_HDR_SIZE)) != E_OK)
276 goto err_ret;
277
278 /* 近隣探索オプション (始点リンクアドレス) */
279 if (nd_opt_off[ND_OPT_OFF_ARRAY_IX(ND_OPT_SOURCE_LINKADDR)]) {
280 opth = (T_ND_OPT_HDR *)((uint8_t *)(input->buf + off + NEIGHBOR_SOLICIT_HDR_SIZE) +
281 nd_opt_off[ND_OPT_OFF_ARRAY_IX(ND_OPT_SOURCE_LINKADDR)] - 8);
282 /* 注意: オプションオフセット配列には、オフセット + 8 が設定されている。*/
283 lladdr = (uint8_t *)(opth + 1);
284 lladdr_len = (opth->len << 3);
285 }
286
287 if (IN6_IS_ADDR_UNSPECIFIED(&ip6h->src) && lladdr != NULL)
288 goto err_ret;
289
290 /* 宛先アドレスがマルチキャストなら近隣通知にデータリンク層のアドレスを付ける。*/
291 if (IN6_IS_ADDR_MULTICAST(&ip6h->dst))
292 tlladdr = true;
293 else
294 tlladdr = false;
295
296 /*
297 * 目的アドレスが、自分のネットワークインタフェースに
298 * 割り当てられているアドレスか調べる。
299 * なお、代理サービスは実装していない。
300 */
301 ifa = in6_lookup_ifaddr(ifp, &nsh->target);
302
303 if (ifa == NULL)
304 goto free_ret;
305
306 /* 探索結果アドレスが重複していれば応答しないで終了する。*/
307 if (ifa->flags & IN6_IFF_DUPLICATED)
308 goto err_ret;
309
310 /*
311 * ネットワークインタフェースのアドレス長が一致しなければエラー
312 */
313 if (lladdr && lladdr_len != ((sizeof(T_IF_ADDR) + sizeof(T_ND_OPT_HDR) + 7) & ~7))
314 goto err_ret;
315
316 /*
317 * 始点アドレスが自分のアドレスと一致すれば重複している。
318 */
319 if (IN6_ARE_ADDR_EQUAL(&ifa->addr, &ip6h->src))
320 goto free_ret;
321
322 /* 重複検出中に、近隣要請を受信したときの処理 */
323 if (ifa->flags & IN6_IFF_TENTATIVE) {
324 if (IN6_IS_ADDR_UNSPECIFIED(&ip6h->src))
325 nd6_dad_ns_input(ifp, ifa);
326 goto free_ret;
327 }
328
329 if ((ifa->flags & IN6_IFF_ANYCAST) || !tlladdr)
330 flags = 0;
331 else
332 flags = ND_NA_FLG_OVERRIDE;
333
334 /*
335 * 始点アドレスが無指定なら、送信相手は重複アドレス検出中で、
336 * 直接送信することはできないので、全ノードマルチキャスト
337 * アドレスに送信する。
338 */
339 if (IN6_IS_ADDR_UNSPECIFIED(&ip6h->src)) {
340 nd6_na_output(ifp, &in6_addr_linklocal_allnodes, &nsh->target, flags, tlladdr);
341 goto free_ret;
342 }
343
344 /* 近隣キャッシュに登録する。*/
345 nd6_cache_lladdr(ifp, &ip6h->src, (T_IF_ADDR *)lladdr, ND_NEIGHBOR_SOLICIT, 0);
346
347 nd6_na_output(ifp, &ip6h->src, &nsh->target, flags | ND_NA_FLG_SOLICITED, tlladdr);
348
349free_ret:
350 syscall(rel_net_buf(input));
351 return;
352
353err_ret:
354 NET_COUNT_ICMP6(net_count_nd6[NC_ICMP6_IN_ERR_PACKETS], 1);
355 syscall(rel_net_buf(input));
356 }
357
358/*
359 * nd6_ns_output -- 近隣要請を出力する。
360 */
361
362void
363nd6_ns_output (T_IFNET *ifp, const T_IN6_ADDR *daddr,
364 const T_IN6_ADDR *taddr, T_LLINFO_ND6 *ln, bool_t dad)
365{
366 T_NEIGHBOR_SOLICIT_HDR *nsh;
367 T_NET_BUF *output;
368 T_IP6_HDR *ip6h;
369 T_IF_ADDR *mac = NULL;
370 uint_t len;
371 uint16_t ipflags = 0;
372
373 NET_COUNT_ICMP6(net_count_nd6[NC_ND6_NS_OUT_PACKETS], 1);
374
375 if (IN6_IS_ADDR_MULTICAST(taddr))
376 return;
377
378 /* 近隣要請ペイロード長を計算する。*/
379 if (!dad && IF_SOFTC_TO_IFADDR(ifp->ic))
380 len = (NEIGHBOR_SOLICIT_HDR_SIZE + ND_OPT_HDR_SIZE + sizeof(T_IF_ADDR) + 7) >> 3 << 3;
381 else
382 len = (NEIGHBOR_SOLICIT_HDR_SIZE + 7) >> 3 << 3;
383
384 /*
385 * 要請マルチキャスト・アドレス宛の近隣探索では、
386 * 他のリンクからの偽造されたデータグラムを
387 * 排除するため、ホップリミットに IPV6_MAXHLIM (255) を設定する。
388 */
389 if (IN6_IS_ADDR_UNSPECIFIED(daddr) || IN6_IS_ADDR_MULTICAST(daddr))
390 ipflags = IPV6_OUT_SET_HOP_LIMIT(IPV6_OUT_FLG_HOP_LIMIT, IPV6_MAXHLIM);
391
392 /* ネットワークバッファを獲得し、IPv6 ヘッダを設定する。*/
393 if (in6_get_datagram(&output, len, 0, daddr, NULL,
394 IPPROTO_ICMPV6, IPV6_MAXHLIM,
395 NBA_SEARCH_ASCENT, TMO_ND6_NS_OUTPUT) != E_OK)
396 return;
397
398 ip6h = GET_IP6_HDR(output);
399
400 if (IN6_IS_ADDR_UNSPECIFIED(daddr)) {
401
402 /* 宛先アドレスが無指定の場合は、要請マルチキャストアドレスを設定する。*/
403 ip6h->dst.s6_addr32[0] = IPV6_ADDR_INT32_MLL;
404 ip6h->dst.s6_addr32[2] = IPV6_ADDR_INT32_ONE;
405 ip6h->dst.s6_addr32[3] = taddr->s6_addr32[3];
406 ip6h->dst.s6_addr8[12] = 0xff;
407 }
408
409 /* 送信元アドレスの設定 */
410 if (!dad) {
411
412 /* 重複アドレス検出ではない時の送信元アドレス決定処理。*/
413 T_IN6_ADDR *saddr;
414
415 if (ln && ln->hold) {
416 /*
417 * 送信がペンディングされているデータグラムの
418 * 送信元アドレスを利用する。
419 */
420 if (ln->hold->len > IF_IP6_HDR_SIZE)
421 saddr = &GET_IP6_HDR(ln->hold)->src;
422 else
423 saddr = NULL;
424 }
425 else
426 saddr = NULL;
427
428 if (saddr && in6_lookup_ifaddr(ifp, saddr))
429 memcpy(&ip6h->src, saddr, sizeof(T_IN6_ADDR));
430 else {
431 /*
432 * 宛先アドレスにふさわしい送信元アドレスを、
433 * ネットワークインタフェースから探索して利用する。
434 */
435 T_IN6_IFADDR *ifa;
436
437 if ((ifa = in6_ifawithifp(ifp, &ip6h->dst)) == NULL) {
438 syscall(rel_net_buf(output));
439 return;
440 }
441 memcpy(&ip6h->src, &ifa->addr, sizeof(T_IN6_ADDR));
442 }
443 }
444 else {
445
446 /* 重複アドレス検出時の送信元アドレスは無指定。*/
447 memset(&ip6h->src, 0, sizeof(T_IN6_ADDR));
448 }
449
450 /* 近隣要請ヘッダを設定する。*/
451 nsh = GET_NEIGHBOR_SOLICIT_HDR(output, IF_IP6_NEIGHBOR_SOLICIT_HDR_OFFSET);
452 nsh->hdr.type = ND_NEIGHBOR_SOLICIT;
453 nsh->hdr.code = 0;
454 nsh->hdr.data.data32 = 0;
455 memcpy(&nsh->target, taddr, sizeof(T_IN6_ADDR));
456
457 if (!dad && (mac = IF_SOFTC_TO_IFADDR(ifp->ic)) != NULL) {
458
459 /* 近隣探索オプションとして、探索目標の MAC アドレスを設定する。*/
460 T_ND_OPT_HDR *opth;
461 uint_t optlen;
462
463 opth = (T_ND_OPT_HDR *)GET_NEIGHBOR_SOLICIT_SDU(output, IF_IP6_NEIGHBOR_SOLICIT_HDR_OFFSET);
464 optlen = (ND_OPT_HDR_SIZE + sizeof(T_IF_ADDR) + 7) >> 3 << 3;
465 memset(opth, 0, optlen);
466 opth->type = ND_OPT_SOURCE_LINKADDR;
467 opth->len = optlen >> 3;
468 memcpy((uint8_t *)opth + sizeof(T_ND_OPT_HDR), mac, sizeof(T_IF_ADDR));
469 }
470
471 /* チェックサムを計算する。*/
472 nsh->hdr.sum = 0;
473 nsh->hdr.sum = in6_cksum(output, IPPROTO_ICMPV6, (uint8_t*)nsh - output->buf, len);
474
475 /* 送信する。*/
476 NET_COUNT_ICMP6(net_count_nd6[NC_ICMP6_OUT_OCTETS],
477 output->len - GET_IF_IP6_HDR_SIZE(output));
478 NET_COUNT_ICMP6(net_count_nd6[NC_ICMP6_OUT_PACKETS], 1);
479 NET_COUNT_MIB(icmp6_ifstat.ipv6IfIcmpOutMsgs, 1);
480 NET_COUNT_MIB(icmp6_ifstat.ipv6IfIcmpOutNeighborSolicits, 1);
481 ip6_output(output, ipflags | IPV6_OUT_FLG_DAD, TMO_ND6_NS_OUTPUT);
482 }
483
484/*
485 * nd6_na_input -- 近隣通知の入力処理。
486 */
487
488void
489nd6_na_input (T_NET_BUF *input, uint_t off)
490{
491 T_IFNET *ifp = IF_GET_IFNET();
492 T_IP6_HDR *ip6h;
493 T_NEIGHBOR_ADVERT_HDR *nah;
494 T_ND_OPT_HDR *opth;
495 T_IN6_IFADDR *ifa;
496 T_LLINFO_ND6 *ln;
497 SYSTIM now;
498 bool_t llchange;
499 uint_t lladdr_len = 0;
500 uint8_t *lladdr = NULL;
501 uint8_t nd_opt_off[ND_OPT_OFF_ARRAY_SIZE];
502
503 NET_COUNT_ICMP6(net_count_nd6[NC_ND6_NS_IN_PACKETS], 1);
504
505 ip6h = GET_IP6_HDR(input);
506
507 /*
508 * ヘッダのチェック、以下の場合は破棄する。
509 * ・ホップリミットが IPV6_MAXHLIM (255) 以外
510 * ・ヘッダ長が短い
511 */
512 if (ip6h->hlim != IPV6_MAXHLIM || input->len - off < NEIGHBOR_ADVERT_HDR_SIZE)
513 goto err_ret;
514
515 nah = (T_NEIGHBOR_ADVERT_HDR *)(input->buf + off);
516
517 /* 目的アドレスがマルチキャストならエラー */
518 if (IN6_IS_ADDR_MULTICAST(&nah->target))
519 goto err_ret;
520
521 /* 近隣要請への応答で、宛先アドレスがマルチキャストならエラー */
522 if ((nah->nd_na_flags_reserved & ND_NA_FLG_SOLICITED) &&
523 IN6_IS_ADDR_MULTICAST(&ip6h->dst))
524 goto err_ret;
525
526 /* 近隣探索オプションのオフセットを記録する。*/
527 if (nd6_options(nd_opt_off, input->buf + (off + NEIGHBOR_ADVERT_HDR_SIZE),
528 input->len - (off + NEIGHBOR_ADVERT_HDR_SIZE)) != E_OK)
529 goto err_ret;
530
531 /* 近隣探索オプション (目的リンクアドレス) */
532 if (nd_opt_off[ND_OPT_OFF_ARRAY_IX(ND_OPT_TARGET_LINKADDR)]) {
533 opth = (T_ND_OPT_HDR *)((uint8_t *)(input->buf + off + NEIGHBOR_ADVERT_HDR_SIZE) +
534 nd_opt_off[ND_OPT_OFF_ARRAY_IX(ND_OPT_TARGET_LINKADDR)] - 8);
535 /* 注意: オプションオフセット配列には、オフセット + 8 が設定されている。*/
536 lladdr = (uint8_t *)(opth + 1);
537 lladdr_len = (opth->len << 3);
538 }
539
540 ifa = in6_lookup_ifaddr(ifp, &nah->target);
541
542 /*
543 * 目的アドレスが自分のネットワークインタフェースに割り当てられているアドレスの
544 * いずれかに一致したときは、重複していることを意味している。
545 */
546 if (ifa) {
547 if (ifa->flags & IN6_IFF_TENTATIVE)
548 nd6_dad_na_input(ifp, ifa);
549 else
550 syslog(LOG_ERROR, "[ND6 NA INPUT] dup IPv6 addr: %s.", ipv62str(NULL, &ifa->addr));
551 goto err_ret;
552 }
553
554 /*
555 * ネットワークインタフェースのアドレス長が一致しなければエラー
556 */
557 if (lladdr && lladdr_len != ((sizeof(T_IF_ADDR) + sizeof(T_ND_OPT_HDR) + 7) & ~7))
558 goto err_ret;
559
560 /* 近隣キャッシュを探索する。*/
561 syscall(wai_sem(SEM_ND6_CACHE));
562 if ((ln = nd6_lookup(&nah->target, false)) == NULL)
563 goto free_ret;
564
565 /* 近隣キャッシュの状態が、データリンク層のアドレス未解決の場合 */
566 if (ln->state == ND6_LLINFO_INCOMPLETE) {
567 /* 通知されたデータリンク層のアドレスの長さが 0 の場合 */
568 if (lladdr == NULL)
569 goto free_ret;
570
571 ln->ifaddr = *(T_IF_ADDR *)lladdr;
572 if (nah->nd_na_flags_reserved & ND_NA_FLG_SOLICITED) {
573 ln->state = ND6_LLINFO_REACHABLE;
574 /*ln->byhint = 0*/;
575 if (ln->expire) {
576 syscall(get_tim(&now));
577 ln->expire = now + ND6_REACHABLE_TIME;
578 }
579 }
580 else {
581 syscall(get_tim(&now));
582 ln->expire = now + ND6_GCOLLECTION_TIME;
583 ln->state = ND6_LLINFO_STALE;
584 }
585
586 if ((nah->nd_na_flags_reserved & ND_NA_FLG_ROUTER) != 0)
587 ln->flags |= ND6_LLIF_ROUTER;
588 else
589 ln->flags &= ~ND6_LLIF_ROUTER;
590 if ((ln->flags & ND6_LLIF_ROUTER) != 0)
591 /*pfxlist_onlink_check()*/;
592 }
593 else {
594 if (lladdr == NULL)
595 llchange = false;
596 else if (memcmp(lladdr, &ln->ifaddr, sizeof(T_IF_ADDR)))
597 llchange = true;
598 else
599 llchange = false;
600
601 /*
602 * 状態遷移表
603 *
604 * nd_na_flags_reserved
605 * OVERRIDE SOLICTED lladdr llchange 処理 (L: lladdr を登録する)
606 *
607 * F F N - (2c)
608 * F F Y F (2b) L
609 * F F Y T (1) REACHABLE -> STALE
610 * F T N - (2c) * -> REACHABLE
611 * F T Y F (2b) L * -> REACHABLE
612 * F T Y T (1) REACHABLE -> STALE
613 * T F N - (2a)
614 * T F Y F (2a) L
615 * T F Y T (2a) L *-> STALE
616 * T T N - (2a) * -> REACHABLE
617 * T T Y F (2a) L * -> REACHABLE
618 * T T Y T (2a) L * -> REACHABLE
619 */
620 if ((nah->nd_na_flags_reserved & ND_NA_FLG_OVERRIDE) == 0 && (lladdr != NULL && llchange)) { /* (1) */
621 /* 状態が REACHABLE なら STALE に遷移する。*/
622 if (ln->state == ND6_LLINFO_REACHABLE) {
623 syscall(get_tim(&now));
624 ln->expire = now + ND6_GCOLLECTION_TIME;
625 ln->state = ND6_LLINFO_STALE;
626 }
627 goto free_ret;
628 }
629 else if ((nah->nd_na_flags_reserved & ND_NA_FLG_OVERRIDE) || /* (2a) */
630 ((nah->nd_na_flags_reserved & ND_NA_FLG_OVERRIDE) == 0 && (lladdr != NULL && !llchange)) || /* (2b) */
631 lladdr == NULL) { /* (2c) */
632
633 /* データリンク層のアドレスが通知されていれば更新する。*/
634 if (lladdr != NULL)
635 ln->ifaddr = *(T_IF_ADDR *)lladdr;
636
637 /* 近隣要請への応答なら REACHABLE に遷移する。*/
638 if (nah->nd_na_flags_reserved & ND_NA_FLG_SOLICITED) {
639 ln->state = ND6_LLINFO_REACHABLE;
640 /*ln->byhint = 0*/;
641 if (ln->expire) {
642 syscall(get_tim(&now));
643 ln->expire = now + ND6_REACHABLE_TIME;
644 }
645 }
646 /*
647 * データリンク層のアドレスが通知され、
648 * 異なるアドレスなら更新する。
649 */
650 else if (lladdr != NULL && llchange) {
651 syscall(get_tim(&now));
652 ln->expire = now + ND6_GCOLLECTION_TIME;
653 ln->state = ND6_LLINFO_STALE;
654 }
655 }
656
657 /* ルータ通知フラグの処理 */
658 if ((ln->flags & ND6_LLIF_ROUTER ) != 0 &&
659 (nah->nd_na_flags_reserved & ND_NA_FLG_ROUTER) == 0) {
660 /*
661 * 送信相手がルータ通知フラグを無効にした場合。
662 * ディフォルト・ルータリストから対象のルータを削除し、
663 * 近隣探索キャッシュを更新する。
664 */
665 T_DEF_ROUTER *dr = NULL;
666
667 syscall(wai_sem(SEM_ND6_DEFRTRLIST));
668 if ((dr = nd6_defrtrlist_lookup(&ln->addr)) != NULL) {
669 nd6_defrtrlist_del(dr);
670 }
671 syscall(sig_sem(SEM_ND6_DEFRTRLIST));
672 }
673 if ((nah->nd_na_flags_reserved & ND_NA_FLG_ROUTER) != 0)
674 ln->flags |= ND6_LLIF_ROUTER;
675 else
676 ln->flags &= ~ND6_LLIF_ROUTER;
677 }
678
679 ln->asked = 0;
680
681 /* アドレス解決待ちのデータグラムがあれば送信する。*/
682 nd6_output_hold(ifp, ln);
683
684free_ret:
685 syscall(sig_sem(SEM_ND6_CACHE));
686 syscall(rel_net_buf(input));
687 return;
688
689err_ret:
690 NET_COUNT_ICMP6(net_count_nd6[NC_ICMP6_IN_ERR_PACKETS], 1);
691 syscall(rel_net_buf(input));
692 }
693
694/*
695 * nd6_na_output -- 近隣通知を出力する。
696 */
697
698void
699nd6_na_output (T_IFNET *ifp, const T_IN6_ADDR *daddr,
700 const T_IN6_ADDR *taddr, uint32_t flags, bool_t tlladdr)
701{
702 T_NEIGHBOR_ADVERT_HDR *nah;
703 T_NET_BUF *output;
704 T_IP6_HDR *ip6h;
705 T_IN6_IFADDR *ifa;
706 T_IF_ADDR *mac = NULL;
707 uint_t len;
708 uint16_t ipflags = 0;
709
710 NET_COUNT_ICMP6(net_count_nd6[NC_ND6_NA_OUT_PACKETS], 1);
711
712 /*
713 * 近隣通知ペイロード長を計算する。
714 * tlladdr が真ならネットワークインタフェースのアドレスを追加する。
715 */
716 if (tlladdr && IF_SOFTC_TO_IFADDR(ifp->ic))
717 len = (NEIGHBOR_ADVERT_HDR_SIZE + ND_OPT_HDR_SIZE + sizeof(T_IF_ADDR) + 7) >> 3 << 3;
718 else
719 len = (NEIGHBOR_ADVERT_HDR_SIZE + 7) >> 3 << 3;
720
721 /*
722 * 他のリンクからの偽造されたデータグラムを
723 * 排除するため、ホップリミットに IPV6_MAXHLIM (255) を設定する。
724 */
725 ipflags = IPV6_OUT_SET_HOP_LIMIT(IPV6_OUT_FLG_HOP_LIMIT, IPV6_MAXHLIM);
726
727 /* ネットワークバッファを獲得し、IPv6 ヘッダを設定する。*/
728 if (in6_get_datagram(&output, len, 0, daddr, NULL,
729 IPPROTO_ICMPV6, IPV6_MAXHLIM,
730 NBA_SEARCH_ASCENT, TMO_ND6_NA_OUTPUT) != E_OK)
731 return;
732
733 ip6h = GET_IP6_HDR(output);
734
735 if (IN6_IS_ADDR_UNSPECIFIED(daddr)) {
736 /*
737 * 宛先アドレスが無指定の場合は、
738 * リンクローカル全ノード・マルチキャストアドレスを設定する。
739 */
740 ip6h->dst.s6_addr32[0] = IPV6_ADDR_INT32_MLL;
741 ip6h->dst.s6_addr32[1] = 0;
742 ip6h->dst.s6_addr32[2] = 0;
743 ip6h->dst.s6_addr32[3] = IPV6_ADDR_INT32_ONE;
744 flags &= ~ND_NA_FLG_SOLICITED;
745 }
746 else
747 memcpy(&ip6h->dst, daddr, sizeof(T_IN6_ADDR));
748
749 /*
750 * 宛先アドレスにふさわしい送信元アドレスを、
751 * ネットワークインタフェースから探索して利用する。
752 */
753 if ((ifa = in6_ifawithifp(ifp, &ip6h->dst)) == NULL) {
754 syscall(rel_net_buf(output));
755 return;
756 }
757 memcpy(&ip6h->src, &ifa->addr, sizeof(T_IN6_ADDR));
758
759 /* 近隣通知ヘッダを設定する。*/
760 nah = GET_NEIGHBOR_ADVERT_HDR(output, IF_IP6_NEIGHBOR_ADVERT_HDR_OFFSET);
761 nah->hdr.type = ND_NEIGHBOR_ADVERT;
762 nah->hdr.code = 0;
763 memcpy(&nah->target, taddr, sizeof(T_IN6_ADDR));
764
765 /* tlladdr が真ならネットワークインタフェースのアドレスを追加する。*/
766 if (tlladdr && (mac = IF_SOFTC_TO_IFADDR(ifp->ic)) != NULL) {
767
768 T_ND_OPT_HDR *opth;
769 uint_t optlen;
770
771 opth = (T_ND_OPT_HDR *)GET_NEIGHBOR_ADVERT_SDU(output, IF_IP6_NEIGHBOR_ADVERT_HDR_OFFSET);
772 optlen = (ND_OPT_HDR_SIZE + sizeof(T_IF_ADDR) + 7) >> 3 << 3;
773 memset(opth, 0, optlen);
774 opth->type = ND_OPT_TARGET_LINKADDR;
775 opth->len = optlen >> 3;
776 memcpy((uint8_t *)opth + sizeof(T_ND_OPT_HDR), mac, sizeof(T_IF_ADDR));
777 }
778 else
779 flags &= ~ND_NA_FLG_OVERRIDE;
780
781 nah->nd_na_flags_reserved = flags;
782
783 /* チェックサムを計算する。*/
784 nah->hdr.sum = 0;
785 nah->hdr.sum = in6_cksum(output, IPPROTO_ICMPV6, (uint8_t*)nah - output->buf, len);
786
787 /* 送信する。*/
788 NET_COUNT_ICMP6(net_count_nd6[NC_ICMP6_OUT_OCTETS],
789 output->len - GET_IF_IP6_HDR_SIZE(output));
790 NET_COUNT_ICMP6(net_count_nd6[NC_ICMP6_OUT_PACKETS], 1);
791 NET_COUNT_MIB(icmp6_ifstat.ipv6IfIcmpOutMsgs, 1);
792 NET_COUNT_MIB(icmp6_ifstat.ipv6IfIcmpOutNeighborAdvertisements, 1);
793 ip6_output(output, ipflags, TMO_ND6_NS_OUTPUT);
794 }
795
796/*
797 * nd6_dad_start -- 重複アドレス検出 (DAD) を開始する。
798 *
799 * 第3引数のtick は、インタフェースが起動されてから、重複検出を
800 * 送信するまでの最小遅延値である。
801 */
802
803void
804nd6_dad_start (T_IFNET *ifp, T_IN6_IFADDR *ifa, int_t *tick)
805{
806 /* フラグをチェックする。*/
807 if (!(ifa->flags & IN6_IFF_TENTATIVE))
808 return;
809
810#if NUM_IP6_DAD_COUNT == 0
811
812 ifa->flags &= ~IN6_IFF_TENTATIVE;
813
814#else /* of #if NUM_IP6_DAD_COUNT == 0 */
815
816 if (ifa->flags & IN6_IFF_ANYCAST) {
817 ifa->flags &= ~IN6_IFF_TENTATIVE;
818 return;
819 }
820 else if ((ifa->flags & IN6_IFF_TENTATIVE) == 0)
821 return;
822
823 /* 重複アドレス検出カウンタをリセットする。*/
824 ifa->ns_icount = ifa->na_icount = ifa->ns_ocount = 0;
825
826 /*
827 * 重複アドレス検出要請を送信する。ただし、インタフェースを
828 * 初期化した直後に、最初に送信する場合は、ランダムな遅延を行う。
829 */
830 if (tick == NULL) {
831 nd6_dad_ns_output(ifp, ifa);
832
833 /* タイムアウトを近隣探索の送信間隔に設定する。*/
834 timeout((callout_func)nd6_dad_timer, ifa, ND6_RETRANS_TIME * NET_TIMER_HZ / SYSTIM_HZ);
835 }
836 else {
837 int_t ntick;
838
839 if (*tick == 0) /* 最初の送信 */
840 ntick = net_rand() % (ND6_FIRST_DAD_DELAY_TIME * NET_TIMER_HZ / SYSTIM_HZ);
841 else
842 ntick = *tick + net_rand() % (ND6_DAD_DELAY_TIME * NET_TIMER_HZ / SYSTIM_HZ);
843 *tick = ntick;
844 timeout((callout_func)nd6_dad_timer, ifa, ntick);
845 }
846
847#endif /* of #if NUM_IP6_DAD_COUNT == 0 */
848
849 }
850
851#endif /* of #ifdef _IP6_CFG */
Note: See TracBrowser for help on using the repository browser.