icmp6.c (19202B)
1 /* 2 * Internet Control Message Protocol for IPv6 3 */ 4 #include "u.h" 5 #include "lib.h" 6 #include "mem.h" 7 #include "dat.h" 8 #include "fns.h" 9 #include "error.h" 10 #include "ip.h" 11 #include "ipv6.h" 12 13 enum 14 { 15 InMsgs6, 16 InErrors6, 17 OutMsgs6, 18 CsumErrs6, 19 LenErrs6, 20 HlenErrs6, 21 HoplimErrs6, 22 IcmpCodeErrs6, 23 TargetErrs6, 24 OptlenErrs6, 25 AddrmxpErrs6, 26 RouterAddrErrs6, 27 28 Nstats6, 29 }; 30 31 enum { 32 ICMP_USEAD6 = 40, 33 }; 34 35 enum { 36 Oflag = 1<<5, 37 Sflag = 1<<6, 38 Rflag = 1<<7, 39 }; 40 41 enum { 42 /* ICMPv6 types */ 43 EchoReply = 0, 44 UnreachableV6 = 1, 45 PacketTooBigV6 = 2, 46 TimeExceedV6 = 3, 47 SrcQuench = 4, 48 ParamProblemV6 = 4, 49 Redirect = 5, 50 EchoRequest = 8, 51 TimeExceed = 11, 52 InParmProblem = 12, 53 Timestamp = 13, 54 TimestampReply = 14, 55 InfoRequest = 15, 56 InfoReply = 16, 57 AddrMaskRequest = 17, 58 AddrMaskReply = 18, 59 EchoRequestV6 = 128, 60 EchoReplyV6 = 129, 61 RouterSolicit = 133, 62 RouterAdvert = 134, 63 NbrSolicit = 135, 64 NbrAdvert = 136, 65 RedirectV6 = 137, 66 67 Maxtype6 = 137, 68 }; 69 70 typedef struct ICMPpkt ICMPpkt; 71 typedef struct IPICMP IPICMP; 72 typedef struct Ndpkt Ndpkt; 73 typedef struct NdiscC NdiscC; 74 75 struct ICMPpkt { 76 uchar type; 77 uchar code; 78 uchar cksum[2]; 79 uchar icmpid[2]; 80 uchar seq[2]; 81 }; 82 83 struct IPICMP { 84 /* Ip6hdr; */ 85 uchar vcf[4]; /* version:4, traffic class:8, flow label:20 */ 86 uchar ploadlen[2]; /* payload length: packet length - 40 */ 87 uchar proto; /* next header type */ 88 uchar ttl; /* hop limit */ 89 uchar src[IPaddrlen]; 90 uchar dst[IPaddrlen]; 91 92 /* ICMPpkt; */ 93 uchar type; 94 uchar code; 95 uchar cksum[2]; 96 uchar icmpid[2]; 97 uchar seq[2]; 98 }; 99 100 struct NdiscC 101 { 102 /* IPICMP; */ 103 /* Ip6hdr; */ 104 uchar vcf[4]; /* version:4, traffic class:8, flow label:20 */ 105 uchar ploadlen[2]; /* payload length: packet length - 40 */ 106 uchar proto; /* next header type */ 107 uchar ttl; /* hop limit */ 108 uchar src[IPaddrlen]; 109 uchar dst[IPaddrlen]; 110 111 /* ICMPpkt; */ 112 uchar type; 113 uchar code; 114 uchar cksum[2]; 115 uchar icmpid[2]; 116 uchar seq[2]; 117 118 uchar target[IPaddrlen]; 119 }; 120 121 struct Ndpkt 122 { 123 /* NdiscC; */ 124 /* IPICMP; */ 125 /* Ip6hdr; */ 126 uchar vcf[4]; /* version:4, traffic class:8, flow label:20 */ 127 uchar ploadlen[2]; /* payload length: packet length - 40 */ 128 uchar proto; /* next header type */ 129 uchar ttl; /* hop limit */ 130 uchar src[IPaddrlen]; 131 uchar dst[IPaddrlen]; 132 133 /* ICMPpkt; */ 134 uchar type; 135 uchar code; 136 uchar cksum[2]; 137 uchar icmpid[2]; 138 uchar seq[2]; 139 140 uchar target[IPaddrlen]; 141 142 uchar otype; 143 uchar olen; /* length in units of 8 octets(incl type, code), 144 * 1 for IEEE 802 addresses */ 145 uchar lnaddr[6]; /* link-layer address */ 146 }; 147 148 typedef struct Icmppriv6 149 { 150 ulong stats[Nstats6]; 151 152 /* message counts */ 153 ulong in[Maxtype6+1]; 154 ulong out[Maxtype6+1]; 155 } Icmppriv6; 156 157 typedef struct Icmpcb6 158 { 159 QLock qlock; 160 uchar headers; 161 } Icmpcb6; 162 163 char *icmpnames6[Maxtype6+1] = 164 { 165 [EchoReply] "EchoReply", 166 [UnreachableV6] "UnreachableV6", 167 [PacketTooBigV6] "PacketTooBigV6", 168 [TimeExceedV6] "TimeExceedV6", 169 [SrcQuench] "SrcQuench", 170 [Redirect] "Redirect", 171 [EchoRequest] "EchoRequest", 172 [TimeExceed] "TimeExceed", 173 [InParmProblem] "InParmProblem", 174 [Timestamp] "Timestamp", 175 [TimestampReply] "TimestampReply", 176 [InfoRequest] "InfoRequest", 177 [InfoReply] "InfoReply", 178 [AddrMaskRequest] "AddrMaskRequest", 179 [AddrMaskReply] "AddrMaskReply", 180 [EchoRequestV6] "EchoRequestV6", 181 [EchoReplyV6] "EchoReplyV6", 182 [RouterSolicit] "RouterSolicit", 183 [RouterAdvert] "RouterAdvert", 184 [NbrSolicit] "NbrSolicit", 185 [NbrAdvert] "NbrAdvert", 186 [RedirectV6] "RedirectV6", 187 }; 188 189 static char *statnames6[Nstats6] = 190 { 191 [InMsgs6] "InMsgs", 192 [InErrors6] "InErrors", 193 [OutMsgs6] "OutMsgs", 194 [CsumErrs6] "CsumErrs", 195 [LenErrs6] "LenErrs", 196 [HlenErrs6] "HlenErrs", 197 [HoplimErrs6] "HoplimErrs", 198 [IcmpCodeErrs6] "IcmpCodeErrs", 199 [TargetErrs6] "TargetErrs", 200 [OptlenErrs6] "OptlenErrs", 201 [AddrmxpErrs6] "AddrmxpErrs", 202 [RouterAddrErrs6] "RouterAddrErrs", 203 }; 204 205 static char *unreachcode[] = 206 { 207 [Icmp6_no_route] "no route to destination", 208 [Icmp6_ad_prohib] "comm with destination administratively prohibited", 209 [Icmp6_out_src_scope] "beyond scope of source address", 210 [Icmp6_adr_unreach] "address unreachable", 211 [Icmp6_port_unreach] "port unreachable", 212 [Icmp6_gress_src_fail] "source address failed ingress/egress policy", 213 [Icmp6_rej_route] "reject route to destination", 214 [Icmp6_unknown] "icmp unreachable: unknown code", 215 }; 216 217 static void icmpkick6(void *x, Block *bp); 218 219 static void 220 icmpcreate6(Conv *c) 221 { 222 c->rq = qopen(64*1024, Qmsg, 0, c); 223 c->wq = qbypass(icmpkick6, c); 224 } 225 226 static void 227 set_cksum(Block *bp) 228 { 229 IPICMP *p = (IPICMP *)(bp->rp); 230 231 hnputl(p->vcf, 0); /* borrow IP header as pseudoheader */ 232 hnputs(p->ploadlen, blocklen(bp) - IP6HDR); 233 p->proto = 0; 234 p->ttl = ICMPv6; /* ttl gets set later */ 235 hnputs(p->cksum, 0); 236 hnputs(p->cksum, ptclcsum(bp, 0, blocklen(bp))); 237 p->proto = ICMPv6; 238 } 239 240 static Block * 241 newIPICMP(int packetlen) 242 { 243 Block *nbp; 244 245 nbp = allocb(packetlen); 246 nbp->wp += packetlen; 247 memset(nbp->rp, 0, packetlen); 248 return nbp; 249 } 250 251 void 252 icmpadvise6(Proto *icmp, Block *bp, char *msg) 253 { 254 ushort recid; 255 Conv **c, *s; 256 IPICMP *p; 257 258 p = (IPICMP *)bp->rp; 259 recid = nhgets(p->icmpid); 260 261 for(c = icmp->conv; *c; c++) { 262 s = *c; 263 if(s->lport == recid && ipcmp(s->raddr, p->dst) == 0){ 264 qhangup(s->rq, msg); 265 qhangup(s->wq, msg); 266 break; 267 } 268 } 269 freeblist(bp); 270 } 271 272 static void 273 icmpkick6(void *x, Block *bp) 274 { 275 uchar laddr[IPaddrlen], raddr[IPaddrlen]; 276 Conv *c = x; 277 IPICMP *p; 278 Icmppriv6 *ipriv = c->p->priv; 279 Icmpcb6 *icb = (Icmpcb6*)c->ptcl; 280 281 if(bp == nil) 282 return; 283 284 if(icb->headers==6) { 285 /* get user specified addresses */ 286 bp = pullupblock(bp, ICMP_USEAD6); 287 if(bp == nil) 288 return; 289 bp->rp += 8; 290 ipmove(laddr, bp->rp); 291 bp->rp += IPaddrlen; 292 ipmove(raddr, bp->rp); 293 bp->rp += IPaddrlen; 294 bp = padblock(bp, sizeof(Ip6hdr)); 295 } 296 297 if(blocklen(bp) < sizeof(IPICMP)){ 298 freeblist(bp); 299 return; 300 } 301 p = (IPICMP *)(bp->rp); 302 if(icb->headers == 6) { 303 ipmove(p->dst, raddr); 304 ipmove(p->src, laddr); 305 } else { 306 ipmove(p->dst, c->raddr); 307 ipmove(p->src, c->laddr); 308 hnputs(p->icmpid, c->lport); 309 } 310 311 set_cksum(bp); 312 p->vcf[0] = 0x06 << 4; 313 if(p->type <= Maxtype6) 314 ipriv->out[p->type]++; 315 ipoput6(c->p->f, bp, 0, c->ttl, c->tos, nil); 316 } 317 318 char* 319 icmpctl6(Conv *c, char **argv, int argc) 320 { 321 Icmpcb6 *icb; 322 323 icb = (Icmpcb6*) c->ptcl; 324 if(argc==1 && strcmp(argv[0], "headers")==0) { 325 icb->headers = 6; 326 return nil; 327 } 328 return "unknown control request"; 329 } 330 331 static void 332 goticmpkt6(Proto *icmp, Block *bp, int muxkey) 333 { 334 ushort recid; 335 uchar *addr; 336 Conv **c, *s; 337 IPICMP *p = (IPICMP *)bp->rp; 338 339 if(muxkey == 0) { 340 recid = nhgets(p->icmpid); 341 addr = p->src; 342 } else { 343 recid = muxkey; 344 addr = p->dst; 345 } 346 347 for(c = icmp->conv; *c; c++){ 348 s = *c; 349 if(s->lport == recid && ipcmp(s->raddr, addr) == 0){ 350 bp = concatblock(bp); 351 if(bp != nil) 352 qpass(s->rq, bp); 353 return; 354 } 355 } 356 357 freeblist(bp); 358 } 359 360 static Block * 361 mkechoreply6(Block *bp, Ipifc *ifc) 362 { 363 uchar addr[IPaddrlen]; 364 IPICMP *p = (IPICMP *)(bp->rp); 365 366 ipmove(addr, p->src); 367 if(!isv6mcast(p->dst)) 368 ipmove(p->src, p->dst); 369 else if (!ipv6anylocal(ifc, p->src)) 370 return nil; 371 ipmove(p->dst, addr); 372 p->type = EchoReplyV6; 373 set_cksum(bp); 374 return bp; 375 } 376 377 /* 378 * sends out an ICMPv6 neighbor solicitation 379 * suni == SRC_UNSPEC or SRC_UNI, 380 * tuni == TARG_MULTI => multicast for address resolution, 381 * and tuni == TARG_UNI => neighbor reachability. 382 */ 383 extern void 384 icmpns(Fs *f, uchar* src, int suni, uchar* targ, int tuni, uchar* mac) 385 { 386 Block *nbp; 387 Ndpkt *np; 388 Proto *icmp = f->t2p[ICMPv6]; 389 Icmppriv6 *ipriv = icmp->priv; 390 391 nbp = newIPICMP(sizeof(Ndpkt)); 392 np = (Ndpkt*) nbp->rp; 393 394 if(suni == SRC_UNSPEC) 395 memmove(np->src, v6Unspecified, IPaddrlen); 396 else 397 memmove(np->src, src, IPaddrlen); 398 399 if(tuni == TARG_UNI) 400 memmove(np->dst, targ, IPaddrlen); 401 else 402 ipv62smcast(np->dst, targ); 403 404 np->type = NbrSolicit; 405 np->code = 0; 406 memmove(np->target, targ, IPaddrlen); 407 if(suni != SRC_UNSPEC) { 408 np->otype = SRC_LLADDR; 409 np->olen = 1; /* 1+1+6 = 8 = 1 8-octet */ 410 memmove(np->lnaddr, mac, sizeof(np->lnaddr)); 411 } else 412 nbp->wp -= sizeof(Ndpkt) - sizeof(NdiscC); 413 414 set_cksum(nbp); 415 np = (Ndpkt*)nbp->rp; 416 np->ttl = HOP_LIMIT; 417 np->vcf[0] = 0x06 << 4; 418 ipriv->out[NbrSolicit]++; 419 netlog(f, Logicmp, "sending neighbor solicitation %I\n", targ); 420 ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil); 421 } 422 423 /* 424 * sends out an ICMPv6 neighbor advertisement. pktflags == RSO flags. 425 */ 426 extern void 427 icmpna(Fs *f, uchar* src, uchar* dst, uchar* targ, uchar* mac, uchar flags) 428 { 429 Block *nbp; 430 Ndpkt *np; 431 Proto *icmp = f->t2p[ICMPv6]; 432 Icmppriv6 *ipriv = icmp->priv; 433 434 nbp = newIPICMP(sizeof(Ndpkt)); 435 np = (Ndpkt*)nbp->rp; 436 437 memmove(np->src, src, IPaddrlen); 438 memmove(np->dst, dst, IPaddrlen); 439 440 np->type = NbrAdvert; 441 np->code = 0; 442 np->icmpid[0] = flags; 443 memmove(np->target, targ, IPaddrlen); 444 445 np->otype = TARGET_LLADDR; 446 np->olen = 1; 447 memmove(np->lnaddr, mac, sizeof(np->lnaddr)); 448 449 set_cksum(nbp); 450 np = (Ndpkt*) nbp->rp; 451 np->ttl = HOP_LIMIT; 452 np->vcf[0] = 0x06 << 4; 453 ipriv->out[NbrAdvert]++; 454 netlog(f, Logicmp, "sending neighbor advertisement %I\n", src); 455 ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil); 456 } 457 458 extern void 459 icmphostunr(Fs *f, Ipifc *ifc, Block *bp, int code, int free) 460 { 461 int osz = BLEN(bp); 462 int sz = MIN(sizeof(IPICMP) + osz, v6MINTU); 463 Block *nbp; 464 IPICMP *np; 465 Ip6hdr *p; 466 Proto *icmp = f->t2p[ICMPv6]; 467 Icmppriv6 *ipriv = icmp->priv; 468 469 p = (Ip6hdr *)bp->rp; 470 471 if(isv6mcast(p->src)) 472 goto clean; 473 474 nbp = newIPICMP(sz); 475 np = (IPICMP *)nbp->rp; 476 477 RLOCK(ifc); 478 if(ipv6anylocal(ifc, np->src)) 479 netlog(f, Logicmp, "send icmphostunr -> s%I d%I\n", 480 p->src, p->dst); 481 else { 482 netlog(f, Logicmp, "icmphostunr fail -> s%I d%I\n", 483 p->src, p->dst); 484 freeblist(nbp); 485 if(free) 486 goto clean; 487 else 488 return; 489 } 490 491 memmove(np->dst, p->src, IPaddrlen); 492 np->type = UnreachableV6; 493 np->code = code; 494 memmove(nbp->rp + sizeof(IPICMP), bp->rp, sz - sizeof(IPICMP)); 495 set_cksum(nbp); 496 np->ttl = HOP_LIMIT; 497 np->vcf[0] = 0x06 << 4; 498 ipriv->out[UnreachableV6]++; 499 500 if(free) 501 ipiput6(f, ifc, nbp); 502 else { 503 ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil); 504 return; 505 } 506 507 clean: 508 RUNLOCK(ifc); 509 freeblist(bp); 510 } 511 512 extern void 513 icmpttlexceeded6(Fs *f, Ipifc *ifc, Block *bp) 514 { 515 int osz = BLEN(bp); 516 int sz = MIN(sizeof(IPICMP) + osz, v6MINTU); 517 Block *nbp; 518 IPICMP *np; 519 Ip6hdr *p; 520 Proto *icmp = f->t2p[ICMPv6]; 521 Icmppriv6 *ipriv = icmp->priv; 522 523 p = (Ip6hdr *)bp->rp; 524 525 if(isv6mcast(p->src)) 526 return; 527 528 nbp = newIPICMP(sz); 529 np = (IPICMP *) nbp->rp; 530 531 if(ipv6anylocal(ifc, np->src)) 532 netlog(f, Logicmp, "send icmpttlexceeded6 -> s%I d%I\n", 533 p->src, p->dst); 534 else { 535 netlog(f, Logicmp, "icmpttlexceeded6 fail -> s%I d%I\n", 536 p->src, p->dst); 537 return; 538 } 539 540 memmove(np->dst, p->src, IPaddrlen); 541 np->type = TimeExceedV6; 542 np->code = 0; 543 memmove(nbp->rp + sizeof(IPICMP), bp->rp, sz - sizeof(IPICMP)); 544 set_cksum(nbp); 545 np->ttl = HOP_LIMIT; 546 np->vcf[0] = 0x06 << 4; 547 ipriv->out[TimeExceedV6]++; 548 ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil); 549 } 550 551 extern void 552 icmppkttoobig6(Fs *f, Ipifc *ifc, Block *bp) 553 { 554 int osz = BLEN(bp); 555 int sz = MIN(sizeof(IPICMP) + osz, v6MINTU); 556 Block *nbp; 557 IPICMP *np; 558 Ip6hdr *p; 559 Proto *icmp = f->t2p[ICMPv6]; 560 Icmppriv6 *ipriv = icmp->priv; 561 562 p = (Ip6hdr *)bp->rp; 563 564 if(isv6mcast(p->src)) 565 return; 566 567 nbp = newIPICMP(sz); 568 np = (IPICMP *)nbp->rp; 569 570 if(ipv6anylocal(ifc, np->src)) 571 netlog(f, Logicmp, "send icmppkttoobig6 -> s%I d%I\n", 572 p->src, p->dst); 573 else { 574 netlog(f, Logicmp, "icmppkttoobig6 fail -> s%I d%I\n", 575 p->src, p->dst); 576 return; 577 } 578 579 memmove(np->dst, p->src, IPaddrlen); 580 np->type = PacketTooBigV6; 581 np->code = 0; 582 hnputl(np->icmpid, ifc->maxtu - ifc->m->hsize); 583 memmove(nbp->rp + sizeof(IPICMP), bp->rp, sz - sizeof(IPICMP)); 584 set_cksum(nbp); 585 np->ttl = HOP_LIMIT; 586 np->vcf[0] = 0x06 << 4; 587 ipriv->out[PacketTooBigV6]++; 588 ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil); 589 } 590 591 /* 592 * RFC 2461, pages 39-40, pages 57-58. 593 */ 594 static int 595 valid(Proto *icmp, Ipifc *ifc, Block *bp, Icmppriv6 *ipriv) 596 { 597 int sz, osz, unsp, n, ttl, iplen; 598 int pktsz = BLEN(bp); 599 uchar *packet = bp->rp; 600 IPICMP *p = (IPICMP *) packet; 601 Ndpkt *np; 602 603 USED(ifc); 604 n = blocklen(bp); 605 if(n < sizeof(IPICMP)) { 606 ipriv->stats[HlenErrs6]++; 607 netlog(icmp->f, Logicmp, "icmp hlen %d\n", n); 608 goto err; 609 } 610 611 iplen = nhgets(p->ploadlen); 612 if(iplen > n - IP6HDR || ((uint)iplen % 1) != 0) { 613 ipriv->stats[LenErrs6]++; 614 netlog(icmp->f, Logicmp, "icmp length %d\n", iplen); 615 goto err; 616 } 617 618 /* Rather than construct explicit pseudoheader, overwrite IPv6 header */ 619 if(p->proto != ICMPv6) { 620 /* This code assumes no extension headers!!! */ 621 netlog(icmp->f, Logicmp, "icmp error: extension header\n"); 622 goto err; 623 } 624 memset(packet, 0, 4); 625 ttl = p->ttl; 626 p->ttl = p->proto; 627 p->proto = 0; 628 if(ptclcsum(bp, 0, iplen + IP6HDR)) { 629 ipriv->stats[CsumErrs6]++; 630 netlog(icmp->f, Logicmp, "icmp checksum error\n"); 631 goto err; 632 } 633 p->proto = p->ttl; 634 p->ttl = ttl; 635 636 /* additional tests for some pkt types */ 637 if (p->type == NbrSolicit || p->type == NbrAdvert || 638 p->type == RouterAdvert || p->type == RouterSolicit || 639 p->type == RedirectV6) { 640 if(p->ttl != HOP_LIMIT) { 641 ipriv->stats[HoplimErrs6]++; 642 goto err; 643 } 644 if(p->code != 0) { 645 ipriv->stats[IcmpCodeErrs6]++; 646 goto err; 647 } 648 649 switch (p->type) { 650 case NbrSolicit: 651 case NbrAdvert: 652 np = (Ndpkt*) p; 653 if(isv6mcast(np->target)) { 654 ipriv->stats[TargetErrs6]++; 655 goto err; 656 } 657 if(optexsts(np) && np->olen == 0) { 658 ipriv->stats[OptlenErrs6]++; 659 goto err; 660 } 661 662 if (p->type == NbrSolicit && 663 ipcmp(np->src, v6Unspecified) == 0) 664 if(!issmcast(np->dst) || optexsts(np)) { 665 ipriv->stats[AddrmxpErrs6]++; 666 goto err; 667 } 668 669 if(p->type == NbrAdvert) 670 if(isv6mcast(np->dst) && 671 (nhgets(np->icmpid) & Sflag)){ 672 ipriv->stats[AddrmxpErrs6]++; 673 goto err; 674 } 675 break; 676 677 case RouterAdvert: 678 if(pktsz - sizeof(Ip6hdr) < 16) { 679 ipriv->stats[HlenErrs6]++; 680 goto err; 681 } 682 if(!islinklocal(p->src)) { 683 ipriv->stats[RouterAddrErrs6]++; 684 goto err; 685 } 686 sz = sizeof(IPICMP) + 8; 687 while (sz+1 < pktsz) { 688 osz = packet[sz+1]; 689 if(osz <= 0) { 690 ipriv->stats[OptlenErrs6]++; 691 goto err; 692 } 693 sz += 8*osz; 694 } 695 break; 696 697 case RouterSolicit: 698 if(pktsz - sizeof(Ip6hdr) < 8) { 699 ipriv->stats[HlenErrs6]++; 700 goto err; 701 } 702 unsp = (ipcmp(p->src, v6Unspecified) == 0); 703 sz = sizeof(IPICMP) + 8; 704 while (sz+1 < pktsz) { 705 osz = packet[sz+1]; 706 if(osz <= 0 || 707 (unsp && packet[sz] == SRC_LLADDR)) { 708 ipriv->stats[OptlenErrs6]++; 709 goto err; 710 } 711 sz += 8*osz; 712 } 713 break; 714 715 case RedirectV6: 716 /* to be filled in */ 717 break; 718 719 default: 720 goto err; 721 } 722 } 723 return 1; 724 err: 725 ipriv->stats[InErrors6]++; 726 return 0; 727 } 728 729 static int 730 targettype(Fs *f, Ipifc *ifc, uchar *target) 731 { 732 Iplifc *lifc; 733 int t; 734 735 RLOCK(ifc); 736 if(ipproxyifc(f, ifc, target)) { 737 RUNLOCK(ifc); 738 return Tuniproxy; 739 } 740 741 for(lifc = ifc->lifc; lifc; lifc = lifc->next) 742 if(ipcmp(lifc->local, target) == 0) { 743 t = (lifc->tentative)? Tunitent: Tunirany; 744 RUNLOCK(ifc); 745 return t; 746 } 747 748 RUNLOCK(ifc); 749 return 0; 750 } 751 752 static void 753 icmpiput6(Proto *icmp, Ipifc *ipifc, Block *bp) 754 { 755 int refresh = 1; 756 char *msg, m2[128]; 757 uchar pktflags; 758 uchar *packet = bp->rp; 759 uchar lsrc[IPaddrlen]; 760 Block *r; 761 IPICMP *p = (IPICMP *)packet; 762 Icmppriv6 *ipriv = icmp->priv; 763 Iplifc *lifc; 764 Ndpkt* np; 765 Proto *pr; 766 767 if(!valid(icmp, ipifc, bp, ipriv) || p->type > Maxtype6) 768 goto raise; 769 770 ipriv->in[p->type]++; 771 772 switch(p->type) { 773 case EchoRequestV6: 774 r = mkechoreply6(bp, ipifc); 775 if(r == nil) 776 goto raise; 777 ipriv->out[EchoReply]++; 778 ipoput6(icmp->f, r, 0, MAXTTL, DFLTTOS, nil); 779 break; 780 781 case UnreachableV6: 782 if(p->code >= nelem(unreachcode)) 783 msg = unreachcode[Icmp6_unknown]; 784 else 785 msg = unreachcode[p->code]; 786 787 bp->rp += sizeof(IPICMP); 788 if(blocklen(bp) < 8){ 789 ipriv->stats[LenErrs6]++; 790 goto raise; 791 } 792 p = (IPICMP *)bp->rp; 793 pr = Fsrcvpcolx(icmp->f, p->proto); 794 if(pr != nil && pr->advise != nil) { 795 (*pr->advise)(pr, bp, msg); 796 return; 797 } 798 799 bp->rp -= sizeof(IPICMP); 800 goticmpkt6(icmp, bp, 0); 801 break; 802 803 case TimeExceedV6: 804 if(p->code == 0){ 805 sprint(m2, "ttl exceeded at %I", p->src); 806 807 bp->rp += sizeof(IPICMP); 808 if(blocklen(bp) < 8){ 809 ipriv->stats[LenErrs6]++; 810 goto raise; 811 } 812 p = (IPICMP *)bp->rp; 813 pr = Fsrcvpcolx(icmp->f, p->proto); 814 if(pr && pr->advise) { 815 (*pr->advise)(pr, bp, m2); 816 return; 817 } 818 bp->rp -= sizeof(IPICMP); 819 } 820 821 goticmpkt6(icmp, bp, 0); 822 break; 823 824 case RouterAdvert: 825 case RouterSolicit: 826 /* using lsrc as a temp, munge hdr for goticmp6 */ 827 if (0) { 828 memmove(lsrc, p->src, IPaddrlen); 829 memmove(p->src, p->dst, IPaddrlen); 830 memmove(p->dst, lsrc, IPaddrlen); 831 } 832 goticmpkt6(icmp, bp, p->type); 833 break; 834 835 case NbrSolicit: 836 np = (Ndpkt*) p; 837 pktflags = 0; 838 switch (targettype(icmp->f, ipifc, np->target)) { 839 case Tunirany: 840 pktflags |= Oflag; 841 /* fall through */ 842 843 case Tuniproxy: 844 if(ipcmp(np->src, v6Unspecified) != 0) { 845 arpenter(icmp->f, V6, np->src, np->lnaddr, 846 8*np->olen-2, 0); 847 pktflags |= Sflag; 848 } 849 if(ipv6local(ipifc, lsrc)) 850 icmpna(icmp->f, lsrc, 851 (ipcmp(np->src, v6Unspecified) == 0? 852 v6allnodesL: np->src), 853 np->target, ipifc->mac, pktflags); 854 else 855 freeblist(bp); 856 break; 857 858 case Tunitent: 859 /* not clear what needs to be done. send up 860 * an icmp mesg saying don't use this address? */ 861 default: 862 freeblist(bp); 863 } 864 break; 865 866 case NbrAdvert: 867 np = (Ndpkt*) p; 868 869 /* 870 * if the target address matches one of the local interface 871 * addresses and the local interface address has tentative bit 872 * set, insert into ARP table. this is so the duplicate address 873 * detection part of ipconfig can discover duplication through 874 * the arp table. 875 */ 876 lifc = iplocalonifc(ipifc, np->target); 877 if(lifc && lifc->tentative) 878 refresh = 0; 879 arpenter(icmp->f, V6, np->target, np->lnaddr, 8*np->olen-2, 880 refresh); 881 freeblist(bp); 882 break; 883 884 case PacketTooBigV6: 885 default: 886 goticmpkt6(icmp, bp, 0); 887 break; 888 } 889 return; 890 raise: 891 freeblist(bp); 892 } 893 894 int 895 icmpstats6(Proto *icmp6, char *buf, int len) 896 { 897 Icmppriv6 *priv; 898 char *p, *e; 899 int i; 900 901 priv = icmp6->priv; 902 p = buf; 903 e = p+len; 904 for(i = 0; i < Nstats6; i++) 905 p = seprint(p, e, "%s: %lud\n", statnames6[i], priv->stats[i]); 906 for(i = 0; i <= Maxtype6; i++) 907 if(icmpnames6[i]) 908 p = seprint(p, e, "%s: %lud %lud\n", icmpnames6[i], 909 priv->in[i], priv->out[i]); 910 /* else 911 p = seprint(p, e, "%d: %lud %lud\n", i, priv->in[i], 912 priv->out[i]); 913 */ 914 return p - buf; 915 } 916 917 918 /* import from icmp.c */ 919 extern int icmpstate(Conv *c, char *state, int n); 920 extern char* icmpannounce(Conv *c, char **argv, int argc); 921 extern char* icmpconnect(Conv *c, char **argv, int argc); 922 extern void icmpclose(Conv *c); 923 924 void 925 icmp6init(Fs *fs) 926 { 927 Proto *icmp6 = smalloc(sizeof(Proto)); 928 929 icmp6->priv = smalloc(sizeof(Icmppriv6)); 930 icmp6->name = "icmpv6"; 931 icmp6->connect = icmpconnect; 932 icmp6->announce = icmpannounce; 933 icmp6->state = icmpstate; 934 icmp6->create = icmpcreate6; 935 icmp6->close = icmpclose; 936 icmp6->rcv = icmpiput6; 937 icmp6->stats = icmpstats6; 938 icmp6->ctl = icmpctl6; 939 icmp6->advise = icmpadvise6; 940 icmp6->gc = nil; 941 icmp6->ipproto = ICMPv6; 942 icmp6->nc = 16; 943 icmp6->ptclsize = sizeof(Icmpcb6); 944 945 Fsproto(fs, icmp6); 946 }