icmp6.c - vx32 - Local 9vx git repository for patches.
(HTM) git clone git://r-36.net/vx32
(DIR) Log
(DIR) Files
(DIR) Refs
---
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 }