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