Add the Inferno license to files we got from Inferno
[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 clean;
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                 freeblist(nbp);
514                 if (free)
515                         goto clean;
516                 else
517                         return;
518         }
519
520         memmove(np->dst, p->src, IPaddrlen);
521         np->type = UnreachableV6;
522         np->code = code;
523         memmove(nbp->rp + sizeof(struct IPICMP), bp->rp,
524                         sz - sizeof(struct IPICMP));
525         set_cksum(nbp);
526         np->ttl = HOP_LIMIT;
527         np->vcf[0] = 0x06 << 4;
528         ipriv->out[UnreachableV6]++;
529
530         if (free)
531                 ipiput6(f, ifc, nbp);
532         else {
533                 ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, NULL);
534                 return;
535         }
536
537 clean:
538         runlock(&ifc->rwlock);
539         freeblist(bp);
540 }
541
542 extern void icmpttlexceeded6(struct Fs *f, struct Ipifc *ifc, struct block *bp)
543 {
544         struct block *nbp;
545         struct IPICMP *np;
546         struct ip6hdr *p;
547         int osz = BLEN(bp);
548         int sz = MIN(sizeof(struct IPICMP) + osz, v6MINTU);
549         struct Proto *icmp = f->t2p[ICMPv6];
550         Icmppriv6 *ipriv = icmp->priv;
551
552         p = (struct ip6hdr *)bp->rp;
553
554         if (isv6mcast(p->src))
555                 return;
556
557         nbp = newIPICMP(sz);
558         np = (struct IPICMP *)nbp->rp;
559
560         if (ipv6anylocal(ifc, np->src)) {
561                 netlog(f, Logicmp, "send icmpttlexceeded6 -> s%I d%I\n", p->src,
562                            p->dst);
563         } else {
564                 netlog(f, Logicmp, "icmpttlexceeded6 fail -> s%I d%I\n", p->src,
565                            p->dst);
566                 return;
567         }
568
569         memmove(np->dst, p->src, IPaddrlen);
570         np->type = TimeExceedV6;
571         np->code = 0;
572         memmove(nbp->rp + sizeof(struct IPICMP), bp->rp,
573                         sz - sizeof(struct IPICMP));
574         set_cksum(nbp);
575         np->ttl = HOP_LIMIT;
576         np->vcf[0] = 0x06 << 4;
577         ipriv->out[TimeExceedV6]++;
578         ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, NULL);
579 }
580
581 extern void icmppkttoobig6(struct Fs *f, struct Ipifc *ifc, struct block *bp)
582 {
583         struct block *nbp;
584         struct IPICMP *np;
585         struct ip6hdr *p;
586         int osz = BLEN(bp);
587         int sz = MIN(sizeof(struct IPICMP) + osz, v6MINTU);
588         struct Proto *icmp = f->t2p[ICMPv6];
589         Icmppriv6 *ipriv = icmp->priv;
590
591         p = (struct ip6hdr *)bp->rp;
592
593         if (isv6mcast(p->src))
594                 return;
595
596         nbp = newIPICMP(sz);
597         np = (struct IPICMP *)nbp->rp;
598
599         if (ipv6anylocal(ifc, np->src)) {
600                 netlog(f, Logicmp, "send icmppkttoobig6 -> s%I d%I\n", p->src, p->dst);
601         } else {
602                 netlog(f, Logicmp, "icmppkttoobig6 fail -> s%I d%I\n", p->src, p->dst);
603                 return;
604         }
605
606         memmove(np->dst, p->src, IPaddrlen);
607         np->type = PacketTooBigV6;
608         np->code = 0;
609         hnputl(np->icmpid, ifc->maxtu - ifc->m->hsize);
610         memmove(nbp->rp + sizeof(struct IPICMP), bp->rp,
611                         sz - sizeof(struct IPICMP));
612         set_cksum(nbp);
613         np->ttl = HOP_LIMIT;
614         np->vcf[0] = 0x06 << 4;
615         ipriv->out[PacketTooBigV6]++;
616         ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, NULL);
617 }
618
619 /*
620  * RFC 2461, pages 39-40, pages 57-58.
621  */
622 static int
623 valid(struct Proto *icmp, struct Ipifc *ifc,
624           struct block *bp, Icmppriv6 * ipriv)
625 {
626         int sz, osz, unsp, n, ttl, iplen;
627         int pktsz = BLEN(bp);
628         uint8_t *packet = bp->rp;
629         struct IPICMP *p = (struct IPICMP *)packet;
630         struct Ndpkt *np;
631
632         n = blocklen(bp);
633         if (n < sizeof(struct IPICMP)) {
634                 ipriv->stats[HlenErrs6]++;
635                 netlog(icmp->f, Logicmp, "icmp hlen %d\n", n);
636                 goto err;
637         }
638
639         iplen = nhgets(p->ploadlen);
640         if (iplen > n - IPV6HDR_LEN || (iplen % 1)) {
641                 ipriv->stats[LenErrs6]++;
642                 netlog(icmp->f, Logicmp, "icmp length %d\n", iplen);
643                 goto err;
644         }
645         // Rather than construct explicit pseudoheader, overwrite IPv6 header
646         if (p->proto != ICMPv6) {
647                 // This code assumes no extension headers!!!
648                 netlog(icmp->f, Logicmp, "icmp error: extension header\n");
649                 goto err;
650         }
651         memset(packet, 0, 4);
652         ttl = p->ttl;
653         p->ttl = p->proto;
654         p->proto = 0;
655         if (ptclcsum(bp, 0, iplen + IPV6HDR_LEN)) {
656                 ipriv->stats[CsumErrs6]++;
657                 netlog(icmp->f, Logicmp, "icmp checksum error\n");
658                 goto err;
659         }
660         p->proto = p->ttl;
661         p->ttl = ttl;
662
663         /* additional tests for some pkt types */
664         if ((p->type == NbrSolicit) ||
665                 (p->type == NbrAdvert) ||
666                 (p->type == RouterAdvert) ||
667                 (p->type == RouterSolicit) || (p->type == RedirectV6)) {
668
669                 if (p->ttl != HOP_LIMIT) {
670                         ipriv->stats[HoplimErrs6]++;
671                         goto err;
672                 }
673                 if (p->code != 0) {
674                         ipriv->stats[IcmpCodeErrs6]++;
675                         goto err;
676                 }
677
678                 switch (p->type) {
679                         case NbrSolicit:
680                         case NbrAdvert:
681                                 np = (struct Ndpkt *)p;
682                                 if (isv6mcast(np->target)) {
683                                         ipriv->stats[TargetErrs6]++;
684                                         goto err;
685                                 }
686                                 if (optexsts(np) && (np->olen == 0)) {
687                                         ipriv->stats[OptlenErrs6]++;
688                                         goto err;
689                                 }
690
691                                 if (p->type == NbrSolicit) {
692                                         if (ipcmp(np->src, v6Unspecified) == 0) {
693                                                 if (!issmcast(np->dst) || optexsts(np)) {
694                                                         ipriv->stats[AddrmxpErrs6]++;
695                                                         goto err;
696                                                 }
697                                         }
698                                 }
699
700                                 if (p->type == NbrAdvert) {
701                                         if ((isv6mcast(np->dst)) && (nhgets(np->icmpid) & Sflag)) {
702                                                 ipriv->stats[AddrmxpErrs6]++;
703                                                 goto err;
704                                         }
705                                 }
706                                 break;
707
708                         case RouterAdvert:
709                                 if (pktsz - sizeof(struct ip6hdr) < 16) {
710                                         ipriv->stats[HlenErrs6]++;
711                                         goto err;
712                                 }
713                                 if (!islinklocal(p->src)) {
714                                         ipriv->stats[RouterAddrErrs6]++;
715                                         goto err;
716                                 }
717                                 sz = sizeof(struct IPICMP) + 8;
718                                 while ((sz + 1) < pktsz) {
719                                         osz = *(packet + sz + 1);
720                                         if (osz <= 0) {
721                                                 ipriv->stats[OptlenErrs6]++;
722                                                 goto err;
723                                         }
724                                         sz += 8 * osz;
725                                 }
726                                 break;
727
728                         case RouterSolicit:
729                                 if (pktsz - sizeof(struct ip6hdr) < 8) {
730                                         ipriv->stats[HlenErrs6]++;
731                                         goto err;
732                                 }
733                                 unsp = (ipcmp(p->src, v6Unspecified) == 0);
734                                 sz = sizeof(struct IPICMP) + 8;
735                                 while ((sz + 1) < pktsz) {
736                                         osz = *(packet + sz + 1);
737                                         if ((osz <= 0) || (unsp && (*(packet + sz) == slladd))) {
738                                                 ipriv->stats[OptlenErrs6]++;
739                                                 goto err;
740                                         }
741                                         sz += 8 * osz;
742                                 }
743                                 break;
744
745                         case RedirectV6:
746                                 //to be filled in
747                                 break;
748
749                         default:
750                                 goto err;
751                 }
752         }
753
754         return 1;
755
756 err:
757         ipriv->stats[InErrors6]++;
758         return 0;
759 }
760
761 static int targettype(struct Fs *f, struct Ipifc *ifc, uint8_t * target)
762 {
763         struct Iplifc *lifc;
764         int t;
765
766         rlock(&ifc->rwlock);
767         if (ipproxyifc(f, ifc, target)) {
768                 runlock(&ifc->rwlock);
769                 return t_uniproxy;
770         }
771
772         for (lifc = ifc->lifc; lifc; lifc = lifc->next) {
773                 if (ipcmp(lifc->local, target) == 0) {
774                         t = (lifc->tentative) ? t_unitent : t_unirany;
775                         runlock(&ifc->rwlock);
776                         return t;
777                 }
778         }
779
780         runlock(&ifc->rwlock);
781         return 0;
782 }
783
784 static void icmpiput6(struct Proto *icmp, struct Ipifc *ipifc, struct block *bp)
785 {
786         uint8_t *packet = bp->rp;
787         struct IPICMP *p = (struct IPICMP *)packet;
788         Icmppriv6 *ipriv = icmp->priv;
789         struct block *r;
790         struct Proto *pr;
791         char *msg, m2[128];
792         struct Ndpkt *np;
793         uint8_t pktflags;
794         uint8_t lsrc[IPaddrlen];
795         int refresh = 1;
796         struct Iplifc *lifc;
797
798         if (!valid(icmp, ipifc, bp, ipriv))
799                 goto raise;
800
801         if (p->type <= Maxtype6)
802                 ipriv->in[p->type]++;
803         else
804                 goto raise;
805
806         switch (p->type) {
807                 case EchoRequestV6:
808                         r = mkechoreply6(bp);
809                         ipriv->out[EchoReply]++;
810                         ipoput6(icmp->f, r, 0, MAXTTL, DFLTTOS, NULL);
811                         break;
812
813                 case UnreachableV6:
814                         if (p->code > 4)
815                                 msg = unreachcode[icmp6_unkn_code];
816                         else
817                                 msg = unreachcode[p->code];
818
819                         bp->rp += sizeof(struct IPICMP);
820                         if (blocklen(bp) < 8) {
821                                 ipriv->stats[LenErrs6]++;
822                                 goto raise;
823                         }
824                         p = (struct IPICMP *)bp->rp;
825                         pr = Fsrcvpcolx(icmp->f, p->proto);
826                         if (pr != NULL && pr->advise != NULL) {
827                                 (*pr->advise) (pr, bp, msg);
828                                 return;
829                         }
830
831                         bp->rp -= sizeof(struct IPICMP);
832                         goticmpkt6(icmp, bp, 0);
833                         break;
834
835                 case TimeExceedV6:
836                         if (p->code == 0) {
837                                 snprintf(m2, sizeof(m2), "ttl exceeded at %I", p->src);
838
839                                 bp->rp += sizeof(struct IPICMP);
840                                 if (blocklen(bp) < 8) {
841                                         ipriv->stats[LenErrs6]++;
842                                         goto raise;
843                                 }
844                                 p = (struct IPICMP *)bp->rp;
845                                 pr = Fsrcvpcolx(icmp->f, p->proto);
846                                 if (pr != NULL && pr->advise != NULL) {
847                                         (*pr->advise) (pr, bp, m2);
848                                         return;
849                                 }
850                                 bp->rp -= sizeof(struct IPICMP);
851                         }
852
853                         goticmpkt6(icmp, bp, 0);
854                         break;
855
856                 case RouterAdvert:
857                 case RouterSolicit:
858                         /* using lsrc as a temp, munge hdr for goticmp6 
859                            memmove(lsrc, p->src, IPaddrlen);
860                            memmove(p->src, p->dst, IPaddrlen);
861                            memmove(p->dst, lsrc, IPaddrlen); */
862
863                         goticmpkt6(icmp, bp, p->type);
864                         break;
865
866                 case NbrSolicit:
867                         np = (struct Ndpkt *)p;
868                         pktflags = 0;
869                         switch (targettype(icmp->f, ipifc, np->target)) {
870                                 case t_unirany:
871                                         pktflags |= Oflag;
872                                         /* fall through */
873
874                                 case t_uniproxy:
875                                         if (ipcmp(np->src, v6Unspecified) != 0) {
876                                                 arpenter(icmp->f, V6, np->src, np->lnaddr,
877                                                                  8 * np->olen - 2, 0);
878                                                 pktflags |= Sflag;
879                                         }
880                                         if (ipv6local(ipifc, lsrc)) {
881                                                 icmpna(icmp->f, lsrc,
882                                                            (ipcmp(np->src, v6Unspecified) ==
883                                                                 0) ? v6allnodesL : np->src, np->target,
884                                                            ipifc->mac, pktflags);
885                                         } else
886                                                 freeblist(bp);
887                                         break;
888
889                                 case t_unitent:
890                                         /* not clear what needs to be done. send up
891                                          * an icmp mesg saying don't use this address? */
892
893                                 default:
894                                         freeblist(bp);
895                         }
896
897                         break;
898
899                 case NbrAdvert:
900                         np = (struct Ndpkt *)p;
901
902                         /* if the target address matches one of the local interface 
903                          * address and the local interface address has tentative bit set, 
904                          * then insert into ARP table. this is so the duplication address 
905                          * detection part of ipconfig can discover duplication through 
906                          * the arp table
907                          */
908                         lifc = iplocalonifc(ipifc, np->target);
909                         if (lifc && lifc->tentative)
910                                 refresh = 0;
911                         arpenter(icmp->f, V6, np->target, np->lnaddr, 8 * np->olen - 2,
912                                          refresh);
913                         freeblist(bp);
914                         break;
915
916                 case PacketTooBigV6:
917
918                 default:
919                         goticmpkt6(icmp, bp, 0);
920                         break;
921         }
922         return;
923
924 raise:
925         freeblist(bp);
926
927 }
928
929 int icmpstats6(struct Proto *icmp6, char *buf, int len)
930 {
931         Icmppriv6 *priv;
932         char *p, *e;
933         int i;
934
935         priv = icmp6->priv;
936         p = buf;
937         e = p + len;
938         for (i = 0; i < Nstats6; i++)
939                 p = seprintf(p, e, "%s: %u\n", statnames6[i], priv->stats[i]);
940         for (i = 0; i <= Maxtype6; i++) {
941                 if (icmpnames6[i])
942                         p = seprintf(p, e, "%s: %u %u\n", icmpnames6[i], priv->in[i],
943                                                  priv->out[i]);
944 /*              else
945                         p = seprintf(p, e, "%d: %u %u\n", i, priv->in[i], priv->out[i]);
946 */
947         }
948         return p - buf;
949 }
950
951 // need to import from icmp.c
952 extern int icmpstate(struct conv *c, char *state, int n);
953 extern char *icmpannounce(struct conv *c, char **argv, int argc);
954 extern char *icmpconnect(struct conv *c, char **argv, int argc);
955 extern void icmpclose(struct conv *c);
956
957 void icmp6newconv(struct Proto *icmp6, struct conv *conv)
958 {
959         /* Fsprotoclone alloc'd our priv struct and attached it to conv already.
960          * Now we need to init it */
961         struct Icmpcb6 *icb = (struct Icmpcb6 *)conv->ptcl;
962         qlock_init(&icb->qlock);
963 }
964
965 void icmp6init(struct Fs *fs)
966 {
967         struct Proto *icmp6 = kzmalloc(sizeof(struct Proto), 0);
968
969         icmp6->priv = kzmalloc(sizeof(Icmppriv6), 0);
970         icmp6->name = "icmpv6";
971         icmp6->connect = icmpconnect;
972         icmp6->announce = icmpannounce;
973         icmp6->state = icmpstate;
974         icmp6->create = icmpcreate6;
975         icmp6->close = icmpclose;
976         icmp6->rcv = icmpiput6;
977         icmp6->stats = icmpstats6;
978         icmp6->ctl = icmpctl6;
979         icmp6->advise = icmpadvise6;
980         icmp6->newconv = icmp6newconv;
981         icmp6->gc = NULL;
982         icmp6->ipproto = ICMPv6;
983         icmp6->nc = 16;
984         icmp6->ptclsize = sizeof(Icmpcb6);
985
986         Fsproto(fs, icmp6);
987 }