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