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