]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/ip/icmp6.c
7335a3c5e69890e55048460944caa54110d9072a
[plan9front.git] / sys / src / 9 / ip / icmp6.c
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
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
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 *
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
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
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*
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
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 *
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
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
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 extern void
422 icmphostunr(Fs *f, Ipifc *ifc, Block *bp, int code, int free)
423 {
424         int osz = BLEN(bp);
425         int sz = MIN(IPICMPSZ + osz, v6MINTU);
426         Block *nbp;
427         IPICMP *np;
428         Ip6hdr *p;
429         Proto *icmp = f->t2p[ICMPv6];
430         Icmppriv6 *ipriv = icmp->priv;
431
432         p = (Ip6hdr *)bp->rp;
433
434         if(isv6mcast(p->src))
435                 goto clean;
436
437         nbp = newIPICMP(sz);
438         np = (IPICMP *)nbp->rp;
439
440         rlock(ifc);
441         if(ipv6anylocal(ifc, np->src))
442                 netlog(f, Logicmp, "send icmphostunr -> src %I dst %I\n",
443                         p->src, p->dst);
444         else {
445                 netlog(f, Logicmp, "icmphostunr fail -> src %I dst %I\n",
446                         p->src, p->dst);
447                 freeblist(nbp);
448                 if(free)
449                         goto clean;
450                 else
451                         return;
452         }
453
454         memmove(np->dst, p->src, IPaddrlen);
455         np->type = UnreachableV6;
456         np->code = code;
457         memmove(nbp->rp + IPICMPSZ, bp->rp, sz - IPICMPSZ);
458         set_cksum(nbp);
459         np->ttl = HOP_LIMIT;
460         np->vcf[0] = 0x06 << 4;
461         ipriv->out[UnreachableV6]++;
462
463         if(free)
464                 ipiput6(f, ifc, nbp);
465         else {
466                 ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil);
467                 return;
468         }
469
470 clean:
471         runlock(ifc);
472         freeblist(bp);
473 }
474
475 extern void
476 icmpttlexceeded6(Fs *f, Ipifc *ifc, Block *bp)
477 {
478         int osz = BLEN(bp);
479         int sz = MIN(IPICMPSZ + osz, v6MINTU);
480         Block *nbp;
481         IPICMP *np;
482         Ip6hdr *p;
483         Proto *icmp = f->t2p[ICMPv6];
484         Icmppriv6 *ipriv = icmp->priv;
485
486         p = (Ip6hdr *)bp->rp;
487
488         if(isv6mcast(p->src))
489                 return;
490
491         nbp = newIPICMP(sz);
492         np = (IPICMP *) nbp->rp;
493
494         if(ipv6anylocal(ifc, np->src))
495                 netlog(f, Logicmp, "send icmpttlexceeded6 -> src %I dst %I\n",
496                         p->src, p->dst);
497         else {
498                 netlog(f, Logicmp, "icmpttlexceeded6 fail -> src %I dst %I\n",
499                         p->src, p->dst);
500                 return;
501         }
502
503         memmove(np->dst, p->src, IPaddrlen);
504         np->type = TimeExceedV6;
505         np->code = 0;
506         memmove(nbp->rp + IPICMPSZ, bp->rp, sz - IPICMPSZ);
507         set_cksum(nbp);
508         np->ttl = HOP_LIMIT;
509         np->vcf[0] = 0x06 << 4;
510         ipriv->out[TimeExceedV6]++;
511         ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil);
512 }
513
514 extern void
515 icmppkttoobig6(Fs *f, Ipifc *ifc, Block *bp)
516 {
517         int osz = BLEN(bp);
518         int sz = MIN(IPICMPSZ + osz, v6MINTU);
519         Block *nbp;
520         IPICMP *np;
521         Ip6hdr *p;
522         Proto *icmp = f->t2p[ICMPv6];
523         Icmppriv6 *ipriv = icmp->priv;
524
525         p = (Ip6hdr *)bp->rp;
526
527         if(isv6mcast(p->src))
528                 return;
529
530         nbp = newIPICMP(sz);
531         np = (IPICMP *)nbp->rp;
532
533         if(ipv6anylocal(ifc, np->src))
534                 netlog(f, Logicmp, "send icmppkttoobig6 -> src %I dst %I\n",
535                         p->src, p->dst);
536         else {
537                 netlog(f, Logicmp, "icmppkttoobig6 fail -> src %I dst %I\n",
538                         p->src, p->dst);
539                 return;
540         }
541
542         memmove(np->dst, p->src, IPaddrlen);
543         np->type = PacketTooBigV6;
544         np->code = 0;
545         hnputl(np->icmpid, ifc->maxtu - ifc->m->hsize);
546         memmove(nbp->rp + IPICMPSZ, bp->rp, sz - IPICMPSZ);
547         set_cksum(nbp);
548         np->ttl = HOP_LIMIT;
549         np->vcf[0] = 0x06 << 4;
550         ipriv->out[PacketTooBigV6]++;
551         ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil);
552 }
553
554 /*
555  * RFC 2461, pages 39-40, pages 57-58.
556  */
557 static int
558 valid(Proto *icmp, Ipifc *ifc, Block *bp, Icmppriv6 *ipriv)
559 {
560         int sz, osz, unsp, n, ttl, iplen;
561         int pktsz = BLEN(bp);
562         uchar *packet = bp->rp;
563         IPICMP *p = (IPICMP *) packet;
564         Ndpkt *np;
565
566         USED(ifc);
567         n = blocklen(bp);
568         if(n < IPICMPSZ) {
569                 ipriv->stats[HlenErrs6]++;
570                 netlog(icmp->f, Logicmp, "icmp hlen %d\n", n);
571                 goto err;
572         }
573
574         iplen = nhgets(p->ploadlen);
575         if(iplen > n - IP6HDR) {
576                 ipriv->stats[LenErrs6]++;
577                 netlog(icmp->f, Logicmp, "icmp length %d\n", iplen);
578                 goto err;
579         }
580
581         /* Rather than construct explicit pseudoheader, overwrite IPv6 header */
582         if(p->proto != ICMPv6) {
583                 /* This code assumes no extension headers!!! */
584                 netlog(icmp->f, Logicmp, "icmp error: extension header\n");
585                 goto err;
586         }
587         memset(packet, 0, 4);
588         ttl = p->ttl;
589         p->ttl = p->proto;
590         p->proto = 0;
591         if(ptclcsum(bp, 0, iplen + IP6HDR)) {
592                 ipriv->stats[CsumErrs6]++;
593                 netlog(icmp->f, Logicmp, "icmp checksum error\n");
594                 goto err;
595         }
596         p->proto = p->ttl;
597         p->ttl = ttl;
598
599         /* additional tests for some pkt types */
600         if (p->type == NbrSolicit   || p->type == NbrAdvert ||
601             p->type == RouterAdvert || p->type == RouterSolicit ||
602             p->type == RedirectV6) {
603                 if(p->ttl != HOP_LIMIT) {
604                         ipriv->stats[HoplimErrs6]++;
605                         goto err;
606                 }
607                 if(p->code != 0) {
608                         ipriv->stats[IcmpCodeErrs6]++;
609                         goto err;
610                 }
611
612                 switch (p->type) {
613                 case NbrSolicit:
614                 case NbrAdvert:
615                         np = (Ndpkt*) p;
616                         if(isv6mcast(np->target)) {
617                                 ipriv->stats[TargetErrs6]++;
618                                 goto err;
619                         }
620                         if(optexsts(np) && np->olen == 0) {
621                                 ipriv->stats[OptlenErrs6]++;
622                                 goto err;
623                         }
624
625                         if (p->type == NbrSolicit &&
626                             ipcmp(np->src, v6Unspecified) == 0)
627                                 if(!issmcast(np->dst) || optexsts(np)) {
628                                         ipriv->stats[AddrmxpErrs6]++;
629                                         goto err;
630                                 }
631
632                         if(p->type == NbrAdvert)
633                                 if(isv6mcast(np->dst) &&
634                                     (nhgets(np->icmpid) & Sflag)){
635                                         ipriv->stats[AddrmxpErrs6]++;
636                                         goto err;
637                                 }
638                         break;
639
640                 case RouterAdvert:
641                         if(pktsz - IP6HDR < 16) {
642                                 ipriv->stats[HlenErrs6]++;
643                                 goto err;
644                         }
645                         if(!islinklocal(p->src)) {
646                                 ipriv->stats[RouterAddrErrs6]++;
647                                 goto err;
648                         }
649                         sz = IPICMPSZ + 8;
650                         while (sz+1 < pktsz) {
651                                 osz = packet[sz+1];
652                                 if(osz <= 0) {
653                                         ipriv->stats[OptlenErrs6]++;
654                                         goto err;
655                                 }
656                                 sz += 8*osz;
657                         }
658                         break;
659
660                 case RouterSolicit:
661                         if(pktsz - IP6HDR < 8) {
662                                 ipriv->stats[HlenErrs6]++;
663                                 goto err;
664                         }
665                         unsp = (ipcmp(p->src, v6Unspecified) == 0);
666                         sz = IPICMPSZ + 8;
667                         while (sz+1 < pktsz) {
668                                 osz = packet[sz+1];
669                                 if(osz <= 0 ||
670                                     (unsp && packet[sz] == SRC_LLADDR)) {
671                                         ipriv->stats[OptlenErrs6]++;
672                                         goto err;
673                                 }
674                                 sz += 8*osz;
675                         }
676                         break;
677
678                 case RedirectV6:
679                         /* to be filled in */
680                         break;
681
682                 default:
683                         goto err;
684                 }
685         }
686         return 1;
687 err:
688         ipriv->stats[InErrors6]++;
689         return 0;
690 }
691
692 static int
693 targettype(Fs *f, Ipifc *ifc, uchar *target)
694 {
695         Iplifc *lifc;
696         int t;
697
698         rlock(ifc);
699         if(ipproxyifc(f, ifc, target)) {
700                 runlock(ifc);
701                 return Tuniproxy;
702         }
703
704         for(lifc = ifc->lifc; lifc; lifc = lifc->next)
705                 if(ipcmp(lifc->local, target) == 0) {
706                         t = (lifc->tentative)? Tunitent: Tunirany;
707                         runlock(ifc);
708                         return t;
709                 }
710
711         runlock(ifc);
712         return 0;
713 }
714
715 static void
716 icmpiput6(Proto *icmp, Ipifc *ipifc, Block *bp)
717 {
718         int refresh = 1;
719         char *msg, m2[128];
720         uchar pktflags;
721         uchar *packet = bp->rp;
722         uchar lsrc[IPaddrlen];
723         Block *r;
724         IPICMP *p = (IPICMP *)packet;
725         Icmppriv6 *ipriv = icmp->priv;
726         Iplifc *lifc;
727         Ndpkt* np;
728         Proto *pr;
729
730         if(!valid(icmp, ipifc, bp, ipriv) || p->type > Maxtype6)
731                 goto raise;
732
733         ipriv->in[p->type]++;
734
735         switch(p->type) {
736         case EchoRequestV6:
737                 r = mkechoreply6(concatblock(bp), ipifc);
738                 if(r == nil)
739                         goto raise;
740                 ipriv->out[EchoReply]++;
741                 ipoput6(icmp->f, r, 0, MAXTTL, DFLTTOS, nil);
742                 break;
743
744         case UnreachableV6:
745                 if(p->code >= nelem(unreachcode))
746                         msg = unreachcode[Icmp6_unknown];
747                 else
748                         msg = unreachcode[p->code];
749
750                 bp->rp += IPICMPSZ;
751                 if(blocklen(bp) < 8){
752                         ipriv->stats[LenErrs6]++;
753                         goto raise;
754                 }
755                 p = (IPICMP *)bp->rp;
756                 pr = Fsrcvpcolx(icmp->f, p->proto);
757                 if(pr != nil && pr->advise != nil) {
758                         (*pr->advise)(pr, bp, msg);
759                         return;
760                 }
761
762                 bp->rp -= IPICMPSZ;
763                 goticmpkt6(icmp, bp, 0);
764                 break;
765
766         case TimeExceedV6:
767                 if(p->code == 0){
768                         sprint(m2, "ttl exceeded at %I", p->src);
769
770                         bp->rp += IPICMPSZ;
771                         if(blocklen(bp) < 8){
772                                 ipriv->stats[LenErrs6]++;
773                                 goto raise;
774                         }
775                         p = (IPICMP *)bp->rp;
776                         pr = Fsrcvpcolx(icmp->f, p->proto);
777                         if(pr && pr->advise) {
778                                 (*pr->advise)(pr, bp, m2);
779                                 return;
780                         }
781                         bp->rp -= IPICMPSZ;
782                 }
783
784                 goticmpkt6(icmp, bp, 0);
785                 break;
786
787         case RouterAdvert:
788         case RouterSolicit:
789                 /* using lsrc as a temp, munge hdr for goticmp6 */
790                 if (0) {
791                         memmove(lsrc, p->src, IPaddrlen);
792                         memmove(p->src, p->dst, IPaddrlen);
793                         memmove(p->dst, lsrc, IPaddrlen);
794                 }
795                 goticmpkt6(icmp, bp, p->type);
796                 break;
797
798         case NbrSolicit:
799                 np = (Ndpkt*) p;
800                 pktflags = 0;
801                 switch (targettype(icmp->f, ipifc, np->target)) {
802                 case Tunirany:
803                         pktflags |= Oflag;
804                         /* fall through */
805
806                 case Tuniproxy:
807                         if(ipcmp(np->src, v6Unspecified) != 0) {
808                                 arpenter(icmp->f, V6, np->src, np->lnaddr,
809                                         8*np->olen-2, 0);
810                                 pktflags |= Sflag;
811                         }
812                         if(ipv6local(ipifc, lsrc))
813                                 icmpna(icmp->f, lsrc,
814                                         (ipcmp(np->src, v6Unspecified) == 0?
815                                                 v6allnodesL: np->src),
816                                         np->target, ipifc->mac, pktflags);
817                         else
818                                 freeblist(bp);
819                         break;
820
821                 case Tunitent:
822                         /* not clear what needs to be done. send up
823                          * an icmp mesg saying don't use this address? */
824                 default:
825                         freeblist(bp);
826                 }
827                 break;
828
829         case NbrAdvert:
830                 np = (Ndpkt*) p;
831
832                 /*
833                  * if the target address matches one of the local interface
834                  * addresses and the local interface address has tentative bit
835                  * set, insert into ARP table. this is so the duplicate address
836                  * detection part of ipconfig can discover duplication through
837                  * the arp table.
838                  */
839                 lifc = iplocalonifc(ipifc, np->target);
840                 if(lifc && lifc->tentative)
841                         refresh = 0;
842                 arpenter(icmp->f, V6, np->target, np->lnaddr, 8*np->olen-2,
843                         refresh);
844                 freeblist(bp);
845                 break;
846
847         case PacketTooBigV6:
848         default:
849                 goticmpkt6(icmp, bp, 0);
850                 break;
851         }
852         return;
853 raise:
854         freeblist(bp);
855 }
856
857 int
858 icmpstats6(Proto *icmp6, char *buf, int len)
859 {
860         Icmppriv6 *priv;
861         char *p, *e;
862         int i;
863
864         priv = icmp6->priv;
865         p = buf;
866         e = p+len;
867         for(i = 0; i < Nstats6; i++)
868                 p = seprint(p, e, "%s: %lud\n", statnames6[i], priv->stats[i]);
869         for(i = 0; i <= Maxtype6; i++)
870                 if(icmpnames6[i])
871                         p = seprint(p, e, "%s: %lud %lud\n", icmpnames6[i],
872                                 priv->in[i], priv->out[i]);
873 /*              else
874                         p = seprint(p, e, "%d: %lud %lud\n", i, priv->in[i],
875                                 priv->out[i]);
876  */
877         return p - buf;
878 }
879
880
881 /* import from icmp.c */
882 extern int      icmpstate(Conv *c, char *state, int n);
883 extern char*    icmpannounce(Conv *c, char **argv, int argc);
884 extern char*    icmpconnect(Conv *c, char **argv, int argc);
885 extern void     icmpclose(Conv *c);
886
887 void
888 icmp6init(Fs *fs)
889 {
890         Proto *icmp6 = smalloc(sizeof(Proto));
891
892         icmp6->priv = smalloc(sizeof(Icmppriv6));
893         icmp6->name = "icmpv6";
894         icmp6->connect = icmpconnect;
895         icmp6->announce = icmpannounce;
896         icmp6->state = icmpstate;
897         icmp6->create = icmpcreate6;
898         icmp6->close = icmpclose;
899         icmp6->rcv = icmpiput6;
900         icmp6->stats = icmpstats6;
901         icmp6->ctl = icmpctl6;
902         icmp6->advise = icmpadvise6;
903         icmp6->gc = nil;
904         icmp6->ipproto = ICMPv6;
905         icmp6->nc = 16;
906         icmp6->ptclsize = sizeof(Icmpcb6);
907
908         Fsproto(fs, icmp6);
909 }