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