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