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