01948fae1e3f09652ce875fa9a45793fb397382f
[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 static void icmpctl6(struct conv *c, char **argv, int argc)
347 {
348         Icmpcb6 *icb = (Icmpcb6*)c->ptcl;
349
350         if ((argc == 1) && strcmp(argv[0], "headers") == 0)
351                 icb->headers = 6;
352         else
353                 error(EINVAL, "unknown command to %s", __func__);
354 }
355
356 static void goticmpkt6(struct Proto *icmp, struct block *bp, int muxkey)
357 {
358         struct conv **c, *s;
359         struct IPICMP *p = (struct IPICMP *)bp->rp;
360         uint16_t recid;
361         uint8_t *addr;
362
363         if (muxkey == 0) {
364                 recid = nhgets(p->icmpid);
365                 addr = p->src;
366         } else {
367                 recid = muxkey;
368                 addr = p->dst;
369         }
370
371         for (c = icmp->conv; *c; c++) {
372                 s = *c;
373                 if (s->lport == recid && ipcmp(s->raddr, addr) == 0) {
374                         bp = concatblock(bp);
375                         if (bp != NULL)
376                                 qpass(s->rq, bp);
377                         return;
378                 }
379         }
380
381         freeblist(bp);
382 }
383
384 static struct block *mkechoreply6(struct block *bp)
385 {
386         struct IPICMP *p = (struct IPICMP *)(bp->rp);
387         uint8_t addr[IPaddrlen];
388
389         ipmove(addr, p->src);
390         ipmove(p->src, p->dst);
391         ipmove(p->dst, addr);
392         p->type = EchoReplyV6;
393         set_cksum(bp);
394         return bp;
395 }
396
397 /*
398  * sends out an ICMPv6 neighbor solicitation
399  *      suni == SRC_UNSPEC or SRC_UNI, 
400  *      tuni == TARG_MULTI => multicast for address resolution,
401  *      and tuni == TARG_UNI => neighbor reachability.
402  */
403
404 extern void
405 icmpns(struct Fs *f, uint8_t * src, int suni, uint8_t * targ, int tuni,
406            uint8_t * mac)
407 {
408         struct block *nbp;
409         struct Ndpkt *np;
410         struct Proto *icmp = f->t2p[ICMPv6];
411         struct Icmppriv6 *ipriv = icmp->priv;
412
413         nbp = newIPICMP(sizeof(struct Ndpkt));
414         np = (struct Ndpkt *)nbp->rp;
415
416         if (suni == SRC_UNSPEC)
417                 memmove(np->src, v6Unspecified, IPaddrlen);
418         else
419                 memmove(np->src, src, IPaddrlen);
420
421         if (tuni == TARG_UNI)
422                 memmove(np->dst, targ, IPaddrlen);
423         else
424                 ipv62smcast(np->dst, targ);
425
426         np->type = NbrSolicit;
427         np->code = 0;
428         memmove(np->target, targ, IPaddrlen);
429         if (suni != SRC_UNSPEC) {
430                 np->otype = SRC_LLADDRESS;
431                 np->olen = 1;   /* 1+1+6 = 8 = 1 8-octet */
432                 memmove(np->lnaddr, mac, sizeof(np->lnaddr));
433         } else {
434                 int r = sizeof(struct Ndpkt) - sizeof(struct NdiscC);
435                 nbp->wp -= r;
436         }
437
438         set_cksum(nbp);
439         np = (struct Ndpkt *)nbp->rp;
440         np->ttl = HOP_LIMIT;
441         np->vcf[0] = 0x06 << 4;
442         ipriv->out[NbrSolicit]++;
443         netlog(f, Logicmp, "sending neighbor solicitation %I\n", targ);
444         ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, NULL);
445 }
446
447 /*
448  * sends out an ICMPv6 neighbor advertisement. pktflags == RSO flags.
449  */
450 extern void
451 icmpna(struct Fs *f, uint8_t * src, uint8_t * dst, uint8_t * targ,
452            uint8_t * mac, uint8_t flags)
453 {
454         struct block *nbp;
455         struct Ndpkt *np;
456         struct Proto *icmp = f->t2p[ICMPv6];
457         Icmppriv6 *ipriv = icmp->priv;
458
459         nbp = newIPICMP(sizeof(struct Ndpkt));
460         np = (struct Ndpkt *)nbp->rp;
461
462         memmove(np->src, src, IPaddrlen);
463         memmove(np->dst, dst, IPaddrlen);
464
465         np->type = NbrAdvert;
466         np->code = 0;
467         np->icmpid[0] = flags;
468         memmove(np->target, targ, IPaddrlen);
469
470         np->otype = TARGET_LLADDRESS;
471         np->olen = 1;
472         memmove(np->lnaddr, mac, sizeof(np->lnaddr));
473
474         set_cksum(nbp);
475         np = (struct Ndpkt *)nbp->rp;
476         np->ttl = HOP_LIMIT;
477         np->vcf[0] = 0x06 << 4;
478         ipriv->out[NbrAdvert]++;
479         netlog(f, Logicmp, "sending neighbor advertisement %I\n", src);
480         ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, NULL);
481 }
482
483 extern void
484 icmphostunr(struct Fs *f, struct Ipifc *ifc,
485                         struct block *bp, int code, int free)
486 {
487         struct block *nbp;
488         struct IPICMP *np;
489         struct ip6hdr *p;
490         int osz = BLEN(bp);
491         int sz = MIN(sizeof(struct IPICMP) + osz, v6MINTU);
492         struct Proto *icmp = f->t2p[ICMPv6];
493         Icmppriv6 *ipriv = icmp->priv;
494
495         p = (struct ip6hdr *)bp->rp;
496
497         if (isv6mcast(p->src))
498                 goto freebl;
499
500         nbp = newIPICMP(sz);
501         np = (struct IPICMP *)nbp->rp;
502
503         rlock(&ifc->rwlock);
504         if (ipv6anylocal(ifc, np->src)) {
505                 netlog(f, Logicmp, "send icmphostunr -> s%I d%I\n", p->src, p->dst);
506         } else {
507                 netlog(f, Logicmp, "icmphostunr fail -> s%I d%I\n", p->src, p->dst);
508                 runlock(&ifc->rwlock);
509                 freeblist(nbp);
510                 goto freebl;
511         }
512
513         memmove(np->dst, p->src, IPaddrlen);
514         np->type = UnreachableV6;
515         np->code = code;
516         memmove(nbp->rp + sizeof(struct IPICMP), bp->rp,
517                         sz - sizeof(struct IPICMP));
518         set_cksum(nbp);
519         np->ttl = HOP_LIMIT;
520         np->vcf[0] = 0x06 << 4;
521         ipriv->out[UnreachableV6]++;
522
523         if (free)
524                 ipiput6(f, ifc, nbp);
525         else
526                 ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, NULL);
527         runlock(&ifc->rwlock);
528 freebl:
529         if (free)
530                 freeblist(bp);
531 }
532
533 extern void icmpttlexceeded6(struct Fs *f, struct Ipifc *ifc, struct block *bp)
534 {
535         struct block *nbp;
536         struct IPICMP *np;
537         struct ip6hdr *p;
538         int osz = BLEN(bp);
539         int sz = MIN(sizeof(struct IPICMP) + osz, v6MINTU);
540         struct Proto *icmp = f->t2p[ICMPv6];
541         Icmppriv6 *ipriv = icmp->priv;
542
543         p = (struct ip6hdr *)bp->rp;
544
545         if (isv6mcast(p->src))
546                 return;
547
548         nbp = newIPICMP(sz);
549         np = (struct IPICMP *)nbp->rp;
550
551         if (ipv6anylocal(ifc, np->src)) {
552                 netlog(f, Logicmp, "send icmpttlexceeded6 -> s%I d%I\n", p->src,
553                            p->dst);
554         } else {
555                 netlog(f, Logicmp, "icmpttlexceeded6 fail -> s%I d%I\n", p->src,
556                            p->dst);
557                 return;
558         }
559
560         memmove(np->dst, p->src, IPaddrlen);
561         np->type = TimeExceedV6;
562         np->code = 0;
563         memmove(nbp->rp + sizeof(struct IPICMP), bp->rp,
564                         sz - sizeof(struct IPICMP));
565         set_cksum(nbp);
566         np->ttl = HOP_LIMIT;
567         np->vcf[0] = 0x06 << 4;
568         ipriv->out[TimeExceedV6]++;
569         ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, NULL);
570 }
571
572 extern void icmppkttoobig6(struct Fs *f, struct Ipifc *ifc, struct block *bp)
573 {
574         struct block *nbp;
575         struct IPICMP *np;
576         struct ip6hdr *p;
577         int osz = BLEN(bp);
578         int sz = MIN(sizeof(struct IPICMP) + osz, v6MINTU);
579         struct Proto *icmp = f->t2p[ICMPv6];
580         Icmppriv6 *ipriv = icmp->priv;
581
582         p = (struct ip6hdr *)bp->rp;
583
584         if (isv6mcast(p->src))
585                 return;
586
587         nbp = newIPICMP(sz);
588         np = (struct IPICMP *)nbp->rp;
589
590         if (ipv6anylocal(ifc, np->src)) {
591                 netlog(f, Logicmp, "send icmppkttoobig6 -> s%I d%I\n", p->src, p->dst);
592         } else {
593                 netlog(f, Logicmp, "icmppkttoobig6 fail -> s%I d%I\n", p->src, p->dst);
594                 return;
595         }
596
597         memmove(np->dst, p->src, IPaddrlen);
598         np->type = PacketTooBigV6;
599         np->code = 0;
600         hnputl(np->icmpid, ifc->maxtu - ifc->m->hsize);
601         memmove(nbp->rp + sizeof(struct IPICMP), bp->rp,
602                         sz - sizeof(struct IPICMP));
603         set_cksum(nbp);
604         np->ttl = HOP_LIMIT;
605         np->vcf[0] = 0x06 << 4;
606         ipriv->out[PacketTooBigV6]++;
607         ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, NULL);
608 }
609
610 /*
611  * RFC 2461, pages 39-40, pages 57-58.
612  */
613 static int
614 valid(struct Proto *icmp, struct Ipifc *ifc,
615           struct block *bp, Icmppriv6 * ipriv)
616 {
617         int sz, osz, unsp, n, ttl, iplen;
618         int pktsz = BLEN(bp);
619         uint8_t *packet = bp->rp;
620         struct IPICMP *p = (struct IPICMP *)packet;
621         struct Ndpkt *np;
622
623         n = blocklen(bp);
624         if (n < sizeof(struct IPICMP)) {
625                 ipriv->stats[HlenErrs6]++;
626                 netlog(icmp->f, Logicmp, "icmp hlen %d\n", n);
627                 goto err;
628         }
629
630         iplen = nhgets(p->ploadlen);
631         if (iplen > n - IPV6HDR_LEN || (iplen % 1)) {
632                 ipriv->stats[LenErrs6]++;
633                 netlog(icmp->f, Logicmp, "icmp length %d\n", iplen);
634                 goto err;
635         }
636         // Rather than construct explicit pseudoheader, overwrite IPv6 header
637         if (p->proto != ICMPv6) {
638                 // This code assumes no extension headers!!!
639                 netlog(icmp->f, Logicmp, "icmp error: extension header\n");
640                 goto err;
641         }
642         memset(packet, 0, 4);
643         ttl = p->ttl;
644         p->ttl = p->proto;
645         p->proto = 0;
646         if (ptclcsum(bp, 0, iplen + IPV6HDR_LEN)) {
647                 ipriv->stats[CsumErrs6]++;
648                 netlog(icmp->f, Logicmp, "icmp checksum error\n");
649                 goto err;
650         }
651         p->proto = p->ttl;
652         p->ttl = ttl;
653
654         /* additional tests for some pkt types */
655         if ((p->type == NbrSolicit) ||
656                 (p->type == NbrAdvert) ||
657                 (p->type == RouterAdvert) ||
658                 (p->type == RouterSolicit) || (p->type == RedirectV6)) {
659
660                 if (p->ttl != HOP_LIMIT) {
661                         ipriv->stats[HoplimErrs6]++;
662                         goto err;
663                 }
664                 if (p->code != 0) {
665                         ipriv->stats[IcmpCodeErrs6]++;
666                         goto err;
667                 }
668
669                 switch (p->type) {
670                         case NbrSolicit:
671                         case NbrAdvert:
672                                 np = (struct Ndpkt *)p;
673                                 if (isv6mcast(np->target)) {
674                                         ipriv->stats[TargetErrs6]++;
675                                         goto err;
676                                 }
677                                 if (optexsts(np) && (np->olen == 0)) {
678                                         ipriv->stats[OptlenErrs6]++;
679                                         goto err;
680                                 }
681
682                                 if (p->type == NbrSolicit) {
683                                         if (ipcmp(np->src, v6Unspecified) == 0) {
684                                                 if (!issmcast(np->dst) || optexsts(np)) {
685                                                         ipriv->stats[AddrmxpErrs6]++;
686                                                         goto err;
687                                                 }
688                                         }
689                                 }
690
691                                 if (p->type == NbrAdvert) {
692                                         if ((isv6mcast(np->dst)) && (nhgets(np->icmpid) & Sflag)) {
693                                                 ipriv->stats[AddrmxpErrs6]++;
694                                                 goto err;
695                                         }
696                                 }
697                                 break;
698
699                         case RouterAdvert:
700                                 if (pktsz - sizeof(struct ip6hdr) < 16) {
701                                         ipriv->stats[HlenErrs6]++;
702                                         goto err;
703                                 }
704                                 if (!islinklocal(p->src)) {
705                                         ipriv->stats[RouterAddrErrs6]++;
706                                         goto err;
707                                 }
708                                 sz = sizeof(struct IPICMP) + 8;
709                                 while ((sz + 1) < pktsz) {
710                                         osz = *(packet + sz + 1);
711                                         if (osz <= 0) {
712                                                 ipriv->stats[OptlenErrs6]++;
713                                                 goto err;
714                                         }
715                                         sz += 8 * osz;
716                                 }
717                                 break;
718
719                         case RouterSolicit:
720                                 if (pktsz - sizeof(struct ip6hdr) < 8) {
721                                         ipriv->stats[HlenErrs6]++;
722                                         goto err;
723                                 }
724                                 unsp = (ipcmp(p->src, v6Unspecified) == 0);
725                                 sz = sizeof(struct IPICMP) + 8;
726                                 while ((sz + 1) < pktsz) {
727                                         osz = *(packet + sz + 1);
728                                         if ((osz <= 0) || (unsp && (*(packet + sz) == slladd))) {
729                                                 ipriv->stats[OptlenErrs6]++;
730                                                 goto err;
731                                         }
732                                         sz += 8 * osz;
733                                 }
734                                 break;
735
736                         case RedirectV6:
737                                 //to be filled in
738                                 break;
739
740                         default:
741                                 goto err;
742                 }
743         }
744
745         return 1;
746
747 err:
748         ipriv->stats[InErrors6]++;
749         return 0;
750 }
751
752 static int targettype(struct Fs *f, struct Ipifc *ifc, uint8_t * target)
753 {
754         struct Iplifc *lifc;
755         int t;
756
757         rlock(&ifc->rwlock);
758         if (ipproxyifc(f, ifc, target)) {
759                 runlock(&ifc->rwlock);
760                 return t_uniproxy;
761         }
762
763         for (lifc = ifc->lifc; lifc; lifc = lifc->next) {
764                 if (ipcmp(lifc->local, target) == 0) {
765                         t = (lifc->tentative) ? t_unitent : t_unirany;
766                         runlock(&ifc->rwlock);
767                         return t;
768                 }
769         }
770
771         runlock(&ifc->rwlock);
772         return 0;
773 }
774
775 static void icmpiput6(struct Proto *icmp, struct Ipifc *ipifc, struct block *bp)
776 {
777         uint8_t *packet = bp->rp;
778         struct IPICMP *p = (struct IPICMP *)packet;
779         Icmppriv6 *ipriv = icmp->priv;
780         struct block *r;
781         struct Proto *pr;
782         char *msg, m2[128];
783         struct Ndpkt *np;
784         uint8_t pktflags;
785         uint8_t lsrc[IPaddrlen];
786         int refresh = 1;
787         struct Iplifc *lifc;
788
789         if (!valid(icmp, ipifc, bp, ipriv))
790                 goto raise;
791
792         if (p->type <= Maxtype6)
793                 ipriv->in[p->type]++;
794         else
795                 goto raise;
796
797         switch (p->type) {
798                 case EchoRequestV6:
799                         r = mkechoreply6(bp);
800                         ipriv->out[EchoReply]++;
801                         ipoput6(icmp->f, r, 0, MAXTTL, DFLTTOS, NULL);
802                         break;
803
804                 case UnreachableV6:
805                         if (p->code > 4)
806                                 msg = unreachcode[icmp6_unkn_code];
807                         else
808                                 msg = unreachcode[p->code];
809
810                         bp->rp += sizeof(struct IPICMP);
811                         if (blocklen(bp) < 8) {
812                                 ipriv->stats[LenErrs6]++;
813                                 goto raise;
814                         }
815                         p = (struct IPICMP *)bp->rp;
816                         pr = Fsrcvpcolx(icmp->f, p->proto);
817                         if (pr != NULL && pr->advise != NULL) {
818                                 (*pr->advise) (pr, bp, msg);
819                                 return;
820                         }
821
822                         bp->rp -= sizeof(struct IPICMP);
823                         goticmpkt6(icmp, bp, 0);
824                         break;
825
826                 case TimeExceedV6:
827                         if (p->code == 0) {
828                                 snprintf(m2, sizeof(m2), "ttl exceeded at %I", p->src);
829
830                                 bp->rp += sizeof(struct IPICMP);
831                                 if (blocklen(bp) < 8) {
832                                         ipriv->stats[LenErrs6]++;
833                                         goto raise;
834                                 }
835                                 p = (struct IPICMP *)bp->rp;
836                                 pr = Fsrcvpcolx(icmp->f, p->proto);
837                                 if (pr != NULL && pr->advise != NULL) {
838                                         (*pr->advise) (pr, bp, m2);
839                                         return;
840                                 }
841                                 bp->rp -= sizeof(struct IPICMP);
842                         }
843
844                         goticmpkt6(icmp, bp, 0);
845                         break;
846
847                 case RouterAdvert:
848                 case RouterSolicit:
849                         /* using lsrc as a temp, munge hdr for goticmp6 
850                            memmove(lsrc, p->src, IPaddrlen);
851                            memmove(p->src, p->dst, IPaddrlen);
852                            memmove(p->dst, lsrc, IPaddrlen); */
853
854                         goticmpkt6(icmp, bp, p->type);
855                         break;
856
857                 case NbrSolicit:
858                         np = (struct Ndpkt *)p;
859                         pktflags = 0;
860                         switch (targettype(icmp->f, ipifc, np->target)) {
861                                 case t_unirany:
862                                         pktflags |= Oflag;
863                                         /* fall through */
864
865                                 case t_uniproxy:
866                                         if (ipcmp(np->src, v6Unspecified) != 0) {
867                                                 arpenter(icmp->f, V6, np->src, np->lnaddr,
868                                                                  8 * np->olen - 2, 0);
869                                                 pktflags |= Sflag;
870                                         }
871                                         if (ipv6local(ipifc, lsrc)) {
872                                                 icmpna(icmp->f, lsrc,
873                                                            (ipcmp(np->src, v6Unspecified) ==
874                                                                 0) ? v6allnodesL : np->src, np->target,
875                                                            ipifc->mac, pktflags);
876                                         } else
877                                                 freeblist(bp);
878                                         break;
879
880                                 case t_unitent:
881                                         /* not clear what needs to be done. send up
882                                          * an icmp mesg saying don't use this address? */
883
884                                 default:
885                                         freeblist(bp);
886                         }
887
888                         break;
889
890                 case NbrAdvert:
891                         np = (struct Ndpkt *)p;
892
893                         /* if the target address matches one of the local interface 
894                          * address and the local interface address has tentative bit set, 
895                          * then insert into ARP table. this is so the duplication address 
896                          * detection part of ipconfig can discover duplication through 
897                          * the arp table
898                          */
899                         lifc = iplocalonifc(ipifc, np->target);
900                         if (lifc && lifc->tentative)
901                                 refresh = 0;
902                         arpenter(icmp->f, V6, np->target, np->lnaddr, 8 * np->olen - 2,
903                                          refresh);
904                         freeblist(bp);
905                         break;
906
907                 case PacketTooBigV6:
908
909                 default:
910                         goticmpkt6(icmp, bp, 0);
911                         break;
912         }
913         return;
914
915 raise:
916         freeblist(bp);
917
918 }
919
920 int icmpstats6(struct Proto *icmp6, char *buf, int len)
921 {
922         Icmppriv6 *priv;
923         char *p, *e;
924         int i;
925
926         priv = icmp6->priv;
927         p = buf;
928         e = p + len;
929         for (i = 0; i < Nstats6; i++)
930                 p = seprintf(p, e, "%s: %u\n", statnames6[i], priv->stats[i]);
931         for (i = 0; i <= Maxtype6; i++) {
932                 if (icmpnames6[i])
933                         p = seprintf(p, e, "%s: %u %u\n", icmpnames6[i], priv->in[i],
934                                                  priv->out[i]);
935 /*              else
936                         p = seprintf(p, e, "%d: %u %u\n", i, priv->in[i], priv->out[i]);
937 */
938         }
939         return p - buf;
940 }
941
942 // need to import from icmp.c
943 extern int icmpstate(struct conv *c, char *state, int n);
944 extern char *icmpannounce(struct conv *c, char **argv, int argc);
945 extern void icmpconnect(struct conv *c, char **argv, int argc);
946 extern void icmpclose(struct conv *c);
947
948 void icmp6newconv(struct Proto *icmp6, struct conv *conv)
949 {
950         /* Fsprotoclone alloc'd our priv struct and attached it to conv already.
951          * Now we need to init it */
952         struct Icmpcb6 *icb = (struct Icmpcb6 *)conv->ptcl;
953         qlock_init(&icb->qlock);
954 }
955
956 void icmp6init(struct Fs *fs)
957 {
958         struct Proto *icmp6 = kzmalloc(sizeof(struct Proto), 0);
959
960         icmp6->priv = kzmalloc(sizeof(Icmppriv6), 0);
961         icmp6->name = "icmpv6";
962         icmp6->connect = icmpconnect;
963         icmp6->announce = icmpannounce;
964         icmp6->state = icmpstate;
965         icmp6->create = icmpcreate6;
966         icmp6->close = icmpclose;
967         icmp6->rcv = icmpiput6;
968         icmp6->stats = icmpstats6;
969         icmp6->ctl = icmpctl6;
970         icmp6->advise = icmpadvise6;
971         icmp6->newconv = icmp6newconv;
972         icmp6->gc = NULL;
973         icmp6->ipproto = ICMPv6;
974         icmp6->nc = 16;
975         icmp6->ptclsize = sizeof(Icmpcb6);
976
977         Fsproto(fs, icmp6);
978 }