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